///////
Search
🎃

Template Call back

Template Callback 적용

jdbcContext로 전략패턴을 주입하는 부분도 반복됩니다. 이 부분도 메소드로 분리하여 반복을 줄일 수 있습니다.
public void executeSql(String sql) { this.workWithStatementStrategy(new StatementStrategy() { @Override public PreparedStatement makePreparedStatement(Connection connection) throws SQLException { PreparedStatement stmt = connection.prepareStatement(sql); return stmt; } }); } public void deleteAll() thorws SQLException { this.executeSql("delte from users"); }
Java
복사
다음과 같이 sql 쿼리만 넘겨주도록 설계할 수 있습니다.
하지만 executeSql은 UserDao에 두기에는 아깝습니다. HospitalDao와 같은 다른 Dao도 사용할 수 있도록 공유 템플릿 클래스에 옮기면 필요할 때마다 가져와서 사용할 수 있습니다.
UserDao.java
public void deleteAll() thorws SQLException { this.jdbcContext.executeSql("delte from users"); }
Java
복사
JdbcContext.java
public void executeSql(String sql) { this.workWithStatementStrategy(new StatementStrategy() { @Override public PreparedStatement makePreparedStatement(Connection connection) throws SQLException { return connection.prepareStatement(sql); } }); }
Java
복사
결국 그림과 같이 UserDao가 JdbcContext를 의존하면서 template과 client, callback을 사용하는 구조가 되었습니다. 이 방식으로 add()에도 적용할 수 있습니다.

JdbcTemplate 적용

jdbc 사용 준비 : 이전에 JdbcContext에 주입 된 것을 JdbcTemplate로 변경하고 생성자를 통해 주입 받는다.
private final JdbcTemplate jdbcTemplate; public UserDao(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); }
Java
복사

deleteAll 수정

이전에 사용한 콜백( StatementStrategy 인터페이스의 makePreparedStatement() 메서드 )은 Template 콜백( PreparedStatementCreator 인터페이스의 createPreparedStatement() 메서드 )과 같음
update, insert, delete 쿼리를 지원하는 메서드
update()는 실행 결과로 변경된 행의 개수를 리턴
익명 클래스를 통해 PreparedStatementCreator 인터페이스를 구현 / 커넥션을 인자로 하는 createPreparedStatement 메서드를 오버라이딩하여 sql 구문을 실행하는 과정
JdbcTemplate는 위의 과정을 쉽게 처리 할 수 있도록 내장 콜백을 사용하는 update()를 지원한다.
// 내장 콜백 함수를 사용 public void deleteAll() { jdbcTemplate.update("delete from users"); }
Java
복사

add() 수정

update()에서 sql구문과 바인딩할 파라미터를 순서에 유의하여 추가만 해주면 된다.
public void add(final User user) { jdbcTemplate.update("insert into users(id,name,password) values (?,?,?)", user.getId(),user.getName(),user.getPassword()); }
Java
복사

getCount() 수정

queryForObject() 사용 : 결과값이 하나 일 때 사용, 반환 값이 객체
public int getCount() throws SQLException { return jdbcTemplate.queryForObject("select count(*) from users",Integer.class); }
Java
복사
파라미터( select count(*) from users ) : PreparedStatment를 만들기 위한 SQL
파라미터 : Integer.class를 넘겨줌으로써 int형의 데이터를 받아옴

SelectById() 수정

queryForObject() 사용 : 결과값이 하나 일 때 사용, 반환 값이 객체
RowMapper 콜백 사용 : ResultSet의 로우와 객체를 매핑 시키는 역활
참고) ResultSet의 로우 하나를 매팽하기 위해 사용하기 때문에 여러 번 호출될 수 있다.
public User selectById(String id) { String sql = "select * from users where id = ?"; return jdbcTemplate.queryForObject(sql, new RowMapper<User>() { @Override public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password")); return user; } }, id); }
Java
복사
파라미터( “select * from users where id = ? ) : PreparedStatment를 만들기 위한 SQL
파라미터 ( RowMapper 콜백) : 현재 ResultSet이 가르키고 있는 로우의 내용을 User에 담아서 리턴
파라미터( new Object[] {id} ) : 바인딩할 값들 ( 여러 개 일 경우 배열 사용 )

getAll() 추가

query() 메서드 사용 : 반환 값이 List<T>
query()는 queryForObject()와 달리 여러개의 로우가 결과로 나오는 경우 사용
public List<User> getAll() { String sql = "select * from users order by id"; return this.jdbcTemplate.query(sql, new RowMapper<User>() { @Override public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password")); return user; } }); }
Java
복사
파라미터( “select * from users order by id) : PreparedStatment를 만들기 위한 SQL
파라미터 : 바인딩할 값들 ( 없다면 생략 가능 )
파라미터 ( RowMapper 콜백 ) : 현재 ResultSet이 가르키고 있는 로우의 내용을 User에 담아서 리턴
최종적으로 RowMapper는 로우의 개수만큼 호출되며 가져온 모든 User를 List에 담아서 리턴 해줍니다.

재사용 가능한 콜백의 분리

중복 제거

// selectById() 와 getAll() 함수 중 RowMapper 중복 존재하므로 분리 RowMapper<User> rowMapper = new RowMapper<User>() { @Override public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password")); return user; } }; public User selectById(String id) { String sql = "select * from users where id = ?"; return jdbcTemplate.queryForObject(sql, rowMapper, id); } public List<User> getAll() { String sql = "select * from users order by id"; return this.jdbcTemplate.query(sql, rowMapper); }
Java
복사

RowMapper

행 단위로 ResultSet의 행을 매핑하기 위해 JdbcTemplate에서 사용하는 인터페이스
예외 처리에 대해 걱정할 필요는 없습니다. ( SQLException은 JdbcTemplate을 호출하여 catch하고 처리 함 )
RowMapper 객체는 일반적으로 무상태( Stateless )이므로 재사용할 수 있음.