Today I Learned_230710

2 분 소요

토비의 스프링 3.1

제네릭스 타입을 사용하여 템플릿/콜백을 범용적으로 만들면 여러 기능들을 쉽게 구현할 수 있다.

변하는 부분이 콜백이 된다!

JdbcTemplate

스프링이 제공하는 JDBC 코드용 기본 템플릿

생성자의 파라미터로 DataSource를 주입받아 사용

update()

  • PreparedStatementCreator타입의 콜백을 받아서 사용
  • 콜백을 받지 않고 내장 콜백을 사용할 수도 있음
  • SQL과 함께 가변인자로 선언된 파라미터를 제공하면 파라미터 바인딩 가능
// 콜백 받아 사용
public void deleteAll() throws SQLException {
        this.jdbcTemplate.update(
                new PreparedStatementCreator() {
                    @Override
                    public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                        return con.prepareStatement("delete from users");
                    }
                }
        );
    }

// 내장 콜백 사용
public void deleteAll() throws SQLException {
        this.jdbcTemplate.update("delete from users");
    }

// 가변인자 파라미터 받아 사용
public void add(final User user) throws SQLException {
        this.jdbcTemplate.update("insert into USER(id, name, password) values (?, ?, ?)",
                user.getId(), user.getName(), user.getPassword());
    }

query()

  • PreparedStatementCreator 콜백과 ResultSetExtractor 콜백을 파라미터로 받아 사용
  • PreparedStatementCrator는 connection을 받아 PreparedStatement를 돌려줌
  • ResultSetExtractor는 템플릿을 수행해 ResultSet을 받아 수행, 그 결과를 돌려줌
  • 두 번의 템플릿/콜백이 동작한다고 보면 된다.
public int getCount() throws SQLException {
        return this.jdbcTemplate.query(new PreparedStatementCreator() {
            @Override
            public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                return con.prepareStatement("select count(*) from USER");
            }
        }, new ResultSetExtractor<Integer>() {
            @Override
            public Integer extractData(ResultSet rs) throws SQLException, DataAccessException {
                rs.next();
                return rs.getInt(1);
            }
        });
    }

queryForObject()

  • ResultSetExtractor는 ResultSet을 한 번 전달받아 알아서 추출작업을 진행하고 최종 결과만 리턴
  • RowMapper는 ResultSet의 로우 하나를 매핑하기 위해 사용
  • queryForObject(String, Object[], RowMapper)는 deprecated됨
public User get(String id) throws SQLException {
        return this.jdbcTemplate.queryForObject("select * from USER where id = ?",
                new Object[] {id},      // SQL에 바인딩할 파라미터 값. 가변인자 대신 배열을 사용
                new RowMapper<User>() {
                    @Override
                    public User mapRow(ResultSet rs, int rowNum) throws SQLException {
                        User user = new User();
                        user.setId(rs.getString("id"));
                        user.setName(rs.getString("name"));
                        user.setPassword(rs.getString("password"));
                        return user;
                    }
                });
    }
  • 한개의 Row만 return할 것을 기대한 함수
  • row의 개수가 하나가 아니라면 예외 발생
    • EmptyResultDataAccessException

query()

public List<User> getAll() {
        return this.jdbcTemplate.query("select * from USER order by id",
                new RowMapper<User>() {
                    @Override
                    public User mapRow(ResultSet rs, int rowNum) throws SQLException {
                        User user = new User();
                        user.setId(rs.getString("id"));
                        user.setName(rs.getString("name"));
                        user.setPassword(rs.getString("password"));
                        return user;
                    }
                });
    }
  • RowMapper는 쿼리의 결과인 ResultSet의 모든 row를 열람하면서 로우마다 RowMapper 콜백을 호출
  • 현재 row의 내용을 User타입 오브젝트에 매핑해서 돌려줌

테스트를 작성할 때 미리 예외상황에 대한 일관성 있는 기준을 정해두고 이를 테스트로 만들어서 검증하자.

로드 존슨 아저씨는 테스트를 작성할 때 항상 네거티브 테스트부터 만들었다고 한다.

템플릿/콜백 패턴을 잘 활용하면 응집도가 높은 코드를 만들 수 있다.

데이터를 어떻게 조작할지 / DB에 어떻게 붙을지 분리할 수 있다.

카테고리:

업데이트: