JdbcTemplate:
。JDBC의CRUD기능에 사용되는공통 코드를템플릿화 하여 구현 및재사용을 통해 편리하게 DB와 상호작용할 수 있도록 돕는 클래스
▶ JDBC의PreparedStatement등을 통해CRUD시 발생하는공통적인 코드를 감소
。Template Method 패턴으로 구현하여JDBC 기능을Template화 함으로써Boiler Plate Code최소화
- 기존
JDBC를 통한CRUD시 발생하는 문제
。CRUD를 수행할때마다 다음처럼 공통적인중복 코드가 발생try( Connection connection = ConnectionUtil.ConnectionDriver.getConnection( ConnectionUtil.DatabaseType.MySql ){ PreparedStatement pstmt = connection.prepareStatement("SQL문"); ResultSet resultSet = pstmt.executeQuery(); if(resultSet.next()) { String name = resultSet.getString("name"); long balance = resultSet.getLong("balance"); System.out.println(name + " " + balance); } else { System.out.println("실패"); } resultSet.close(); pstmt.close(); }▶
JdbcTemplate는 이러한 불필요한중복코드로 인한복잡도를 최소화하기 위해 해당코드를템플릿 화한메서드를 제공
JdbcTemplate구현
- Connection Pool 정의
함수형 인터페이스정의
。다양한매개변수에도다형성을 통해범용적으로 대응하도록함수형 인터페이스를 정의@FunctionalInterface public interface RowMapper<T> { T map(ResultSet rs) throws SQLException; }▶
ResultSet객체의데이터를자바객체에Mapping하는 역할의메서드@FunctionalInterface public interface StatementPlacementBinder { void set(PreparedStatement stmt) throws SQLException; }▶
SQL문의?에매개변수를Binding하는 역할의메서드
도메인정의
。VO 역할을 수행하는클래스정의public record Product( Long id, String name, Integer price, Integer stock ){ }
JdbcTemplate 메서드정의하기
。각메서드는함수형 인터페이스 구현체를매개변수로 받는 고차함수
▶결합도를 낮추기 위한 용도로메서드 호출시 필요한인터페이스 구현체를 입력하여 활용
SELECT:다건조회구현하기
。각각의 입력받은함수형 인터페이스 구현체의추상 메서드( =함수)를 이용하여 함수형 프로그래밍을 수행함으로써결합도를 낮추면서 기능 구현
▶매개변수가 존재하는 경우BindingParameter로바인딩하는함수를 포함하는인터페이스 구현체입력
▶조회를 통해 도출한ResultSet을자바객체로매핑하는함수를 포함하는인터페이스 구현체입력
。 이후호출하는 쪽에서 동적으로 다양한 상황에 맞는람다식을 정의하도록 설정하여 활용public <T> List<T> queryForList( String query, StatementPlacementBinder pss, RowMapper<T> mapper ){ // try ~ with 를 통해 DB Connection / PreparedStatement 정의 // 이후 자동 해제 try( Connection connection = dataSource.getConnection(); PreparedStatement pstmt = connection.prepareStatement(query); ){ // 매개변수가 존재하는 경우 // 함수형 인터페이스 구현체의 메서드를 통해 매개변수 매핑 수행 설정 if (pss!=null){ pss.set(pstmt); } // SQL문 실행 후 ResultSet으로 조회결과 도출 try(ResultSet rs = pstmt.executeQuery()){ List<T> result = new ArrayList<>(); // 함수형 인터페이스 구현체의 메서드를 통해 ResultSet객체에서 자바객체로 바인딩 while(rs.next()){ result.add(mapper.map(rs)); } return result; } } catch ( SQLException e ){ throw new RuntimeException(e); } }
SELECT : 단건조회구현하기// 단건조회를 처리하는 메서드 public <T> T queryForObject( String query, StatementPlacementBinder pss, RowMapper<T> mapper ){ // 다건조회 메서드 재활용 List<T> result = queryForList(query, pss, mapper); // 조회된 데이터가 없는 경우 if (result.isEmpty()) throw new RuntimeException("No Results"); // 조회된 데이터가 1개 이상인 경우 if(result.size()>1) throw new RuntimeException("Too Many Results"); // return result.getFirst(); }
INSERT:데이터 입력
。DB에데이터 입력하여 해당하는row를save및PK를 반환하도록 설정public Long insertAndReturnKey( String query, StatementPlacementBinder pss ){ try( Connection connection = dataSource.getConnection(); PreparedStatement pstmt = connection.prepareStatement(query); ){ // 함수형 인터페이스의 함수 정의 if (pss!=null){ pss.set(pstmt); } pstmt.executeUpdate(); try( ResultSet rs = pstmt.getGeneratedKeys() ){ if(rs.next()) return rs.getLong(1); else return null; } } catch ( SQLException e ){ throw new RuntimeException(e); } }
DAO 역할을 수행하는클래스정의
。Repository Class정의
。위에서 정의한JdbcTemplate 객체를 생성 및JdbcTemplate 메서드에람다식을 동적으로 할당하여매개변수 바인딩 / 자바객체 매핑을 수행
。save() 메서드에서객체를 반환하는 이유 ?
▶DAO 패턴에 의해PK값 없는 Entity 객체 생성및DB에서PK를 할당받으면서save후DB로부터 다시PK값을 받아서 포함하여Entity 객체를 재생성 후 반환하도록 설정public class ProductRepository { private final JdbcTemplate jdbcTemplate; public ProductRepository(){ this.jdbcTemplate = new JdbcTemplate( DataSourceConfiguration.getDataSource() ); } public List<Product> findAll(){ String sql = """ SELECT * FROM product; """; return jdbcTemplate.queryForList( sql, null, (rs)->{ return new Product( rs.getLong("id"), rs.getString("name"), rs.getInt("price"), rs.getInt("stock") ); } ); } public Product save( Product product ){ String sql = """ INSERT INTO product (name, price, stock) VALUES (?, ?, ?); """; Long savedId = jdbcTemplate.insertAndReturnKey( sql, (pss) -> { pss.setString(1, product.name()); pss.setInt(2, product.price()); pss.setInt(3, product.stock()); } ); return new Product( savedId, product.name(), product.price(), product.stock() ); } public Product findById(int id){ String sql = """ SELECT * FROM product WHERE id = ?; """; return jdbcTemplate.queryForObject( sql, pss -> pss.setLong(1, id), rs -> new Product( rs.getLong("id"), rs.getString("name"), rs.getInt("price"), rs.getInt("stock") ) ); } }