여기에 생성 부분을 내가 만든 JdbcTemplate으로 변경해 볼 것이다.
// KeyHolder는 단순하게 만든다.
public class MyKeyHolder {
private Long id;
protected void setId(long id) { this.id = id; }
public Long getId() { return id; }
}
// JdbcTemplate에서 제공하는 것도 MyPreparedStatementCreator에 이름만 바꿔서 생성했다.
@FunctionalInterface
public interface MyPreparedStatementCreator {
PreparedStatement createPreparedStatement(Connection con) throws SQLException;
}
public class MyJdbcTemplate {
// 단순하다. ps를 만들어주면 실행하고 반환해주면 된다. ID는 홀더에 넣어주고.
public int update(Connection conn, MyPreparedStatementCreator psc, KeyHolder keyHolder) {
try(PreparedStatement ps = psc.createPreparedStatement(conn)) {
int result = ps.executeUpdate();
keyHolder.setId(findGeneratedId(ps));
return result;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
private long findGeneratedId(PreparedStatement ps) throws SQLException {
try (ResultSet keyResultSet = ps.getGeneratedKeys()) {
if(!keyResultSet.next())
throw new SQLException("생성된 키가 없음");
return keyResultSet.getLong(1);
}
}
...
}
JdbcTemplate을 구현하는게 아니기 때문에 KeyHolder는 단순하게 만들고 넘어간다.
public Foods save(Foods food) {
try (Connection conn = DriverManager.getConnection(dbConfig.getUrl(), dbConfig.getUsername(), dbConfig.getPassword());
PreparedStatement ps = conn.prepareStatement("""
INSERT INTO foods(name, price, stock)
VALUES
(?, ?, ?)
""", Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, food.getName());
ps.setInt(2, food.getPrice());
ps.setInt(3, food.getStock());
ps.executeUpdate();
try(ResultSet rs = ps.getGeneratedKeys()) {
rs.next();
long foodId = rs.getLong(1);
MyEntityIdInjector.injectId(food, foodId);
return food;
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public Foods save(Foods food) {
try (Connection conn = DriverManager.getConnection(dbConfig.getUrl(), dbConfig.getUsername(), dbConfig.getPassword())) {
MyKeyHolder myKeyHolder = new MyKeyHolder();
myJdbcTemplate.update(conn, con -> {
PreparedStatement ps = conn.prepareStatement("""
INSERT INTO foods(name, price, stock)
VALUES
(?, ?, ?)
""", Statement.RETURN_GENERATED_KEYS);
ps.setString(1, food.getName());
ps.setInt(2, food.getPrice());
ps.setInt(3, food.getStock());
return ps;
}, myKeyHolder);
MyEntityIdInjector.injectId(food, myKeyHolder.getId());
return food;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
헷갈리지 말자.
repository이기 때문에 내부 구현 기술인 PreparedStatement가 있어도 상관없다.
확실히 try-catch문이 많이 사라지고 있다.
처음 시작할 때 테스트 코드 꼭 작성하자.
처음에 좀 귀찮아도 작성해 놓으니까 잘 동작하는지 체크하기 쉽다.