공유리소스 반환에 대한 문제 -> 예외 발생시점에서 리소스 반환이 안되는 경우 DB풀이 차기 때문에 문제가 발생할 수 있다. -> try/catch/finally 사용이 필수 -> 1.7이후 AutoClose 인터페이스 경우 : try~with~catch 구문을 활용하는게 이득 -> 리소스 반환은 역순으로 close()
2 변하는 것과 변하지 않는 것
변하는 부분과 변하지 않는 부분을 분리 (기능단위 메서드 분리) -> 템플릿메서드 패턴 활용 / 전략 패턴
// 분리 전privatevoidexecuteSql(String sql){this.jdbcContext.workWithStatementStrategy(connection ->connection.prepareStatement("delete from test")); }// 분리 후 (변경되는 부분 -> sql 만 분리)publicvoiddeleteAll() throws SQLException{executeSql("delete from test"); }// 메서드 분리 -> 변하지 않음privatevoidexecuteSql(String sql){this.jdbcContext.workWithStatementStrategy(connection ->connection.prepareStatement(sql)); }
// 사용은 책 참고publicinterfaceLineCallBack<T> {TdoSomething(String line,T value);}
6 스프링의 JdbcTemplate
위에서 만든 JdbcContext 클래스 대신 사용
publicclassUserDao {privateDataSource dataSource;privateJdbcTemplate JdbcTemplate;...publicvoidsetDataSource(DataSource dataSource){this.dataSource= dataSource;this.JdbcTemplate=newJdbcTemplate(this.dataSource); }// JdbcTemplate 사용 -> JdbcContext와 동일 구조publicvoiddeleteAll2() {jdbcTemplate.update(connection ->connection.prepareStatement("delete from test")); }// sql문만 넘김 -> 내장 콜백 사용publicvoiddeleteAll3() {jdbcTemplate.update("delete from test"); }// add 기존 -> AddStatement 전략 클래스 @OverridepublicPreparedStatementmakePreparedStatement(Connection connection) throwsSQLException {PreparedStatement ps =connection.prepareStatement("insert into test values (?,?,?)");ps.setString(1,user.getId());ps.setString(2,user.getName());ps.setString(3,user.getPassword());return ps; }// addpublicvoidadd2(User user) {jdbcTemplate.update("inset into test values(?,?,?)",user.getId(),user.getName(),user.getPassword()); }}// Add 테스트 (JdbcTemplate) @Testpublicvoidtest_3_47() {userDao.add2(newUser("1","so","1234")); }// Delete 테스트 (JdbcTemplate) @Testpublicvoidtest_3_46() {userDao.deleteAll2(); }
queryForInt -> deprecated
// 내장 콜백 xpublicintgetCount() {returnjdbcTemplate.query( connection ->connection.prepareStatement("select count(*) from test"),// 첫번째 파라미터 : PreparedStatementCreator resultSet -> { // 두번째 파라미터 : ResultSetExtractorif (resultSet.next()) {returnresultSet.getInt(1); }return0; }); }// 디프리케이트 돼서 queryForInt -> queryForObject로 수정// 내장콜백 사용publicintgetCount2() {returnjdbcTemplate.queryForObject("select count(*) from test",Integer.class); }
queryForObject
RowMapper
// 1.8이전 기준publicUserget2(String id) {returnjdbcTemplate.queryForObject("select * from test where id = ?",newObject[]{id},newRowMapper<User>() { @OverridepublicUsermapRow(ResultSet resultSet,int i) throwsSQLException {User user =newUser();user.setId(resultSet.getString("id"));user.setName(resultSet.getString("name"));user.setPassword(resultSet.getString("password"));return user; } }); }// 1.8이상publicUserget22(String id) {returnjdbcTemplate.queryForObject("select * from test where id = ?",newObject[]{id}, (resultSet, i) -> {User user =newUser();user.setId(resultSet.getString("id"));user.setName(resultSet.getString("name"));user.setPassword(resultSet.getString("password"));return user; } ); }
getAll
query() 같은 경우 데이터가 없으면 빈값을 리턴해줌
queryForObject() 같은 경우는 데이터가 없는 경우 예외발생
publicList<User>getAll() {returnjdbcTemplate.query("select * from test order by id", (resultSet, i) -> {User user =newUser();user.setId(resultSet.getString("id"));user.setName(resultSet.getString("name"));user.setPassword(resultSet.getString("password"));return user; }); }
리팩토링 (중복되는 부분 제거)
```java // 중복되는 부분 메서드 추출 private RowMapper getRowMapper() { return (ResultSet rs, int rowNum) -> { User user = new User(); user.setId(rs.getString("id")); user.setName(rs.getString("name")); user.setPassword(rs.getString("password")); return user; }; }
public List getAllRefactoring() { return jdbcTemplate.query("select * from test order by id", getRowMapper() // private 사용 ); }
@Test public void test_3_56() { System.out.println(userDao.getAllRefactoring()); // 리팩토링 후 맞는지 확인 Assert.assertThat(userDao.getAll().toString(), CoreMatchers.is(userDao.getAllRefactoring().toString())); }