Java - JDBC Template

TopOfTheHead·2026년 4월 17일

자바 ( JAVA )

목록 보기
26/28

JdbcTemplate :
JDBCCRUD 기능에 사용되는 공통 코드템플릿화 하여 구현 및 재사용을 통해 편리하게 DB와 상호작용할 수 있도록 돕는 클래스
JDBCPreparedStatement 등을 통해 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데이터 입력하여 해당하는 rowsavePK를 반환하도록 설정
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를 할당받으면서 saveDB로부터 다시 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")
			)
		);
	}
}
profile
공부기록 블로그

0개의 댓글