본문 바로가기
공부방

[TIL 06/28] Connection Pool, DataSource, JdbcTemplate

by hseong 2023. 6. 30.

1. Connection Pool

자바 애플리케이션에서 DB에 쿼리를 날리기 위해 매 요청마다 커넥션을 생성하는 것은 많은 리소스가 소비된다. SQL을 실행하는 시간뿐만 아니라 커넥션을 새롭게 만드는데도 많은 시간이 소요되어 요청에 대한 응답 속도에 영향을 주게 된다.

이러한 문제를 해결하기 위해 커넥션 풀이 등장하였다. 커넥션을 미리 생성해두었다가 필요할 때마다 하나씩 꺼내 쓰고 작업이 종료되면 커넥션 풀에 다시 반환한다.

2. DataSource

DataSource는 커넥션을 획득하는 방법에 대해 추상화한 인터페이스이다.

개발자는 커넥션이 DriverManager를 이용해서 만들어지는지, 커넥션 풀을 통해 획득하는지 전혀 알 필요 없이 커넥션을 획득하고, 사용하고, 종료하면 된다.

3. JdbcTemplate

기존에 JDBC API만을 가지고 코드를 작성할 때는 다음과 같이 중복되는 작업을 수행해야 했다.

  • 커넥션 획득
  • while(resultSet.next())루프를 통한 결과 조회
  • 커넥션 종료, statement, resultSet 종료
  • 예외 처리

JdbcTempate은 템플릿 콜백 패턴을 사용해서 이러한 반복적인 작업을 대신 처리해준다.

@Repository
public class CustomerJdbcRepository implements CustomerRepository {

    private final JdbcTemplate jdbcTemplate;

    public CustomerJdbcRepository(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @Override
    public void save(Customer customer) {
        String sql = "insert into customers(customer_id, name) values (?, ?)";
        int update = jdbcTemplate.update(con -> {
            PreparedStatement pstmt = con.prepareStatement(sql);
            pstmt.setString(1, customer.getCustomerId().toString());
            pstmt.setString(2, customer.getName());
            return pstmt;
        });
    }

    @Override
    public Optional<Customer> findById(UUID customerId) {
        String sql = "select * from customers where customer_id = ?";
        Customer customer = jdbcTemplate.queryForObject(sql, customerRowMapper(), customerId.toString());
        return Optional.ofNullable(customer);
    }

    @Override
    public List<Customer> findAll() {
        String sql = "select * from customers";
        return jdbcTemplate.query(sql, customerRowMapper());
    }

    @Override
    public void update(Customer customer) {
        String sql = "update customers set name = ? where customer_id = ?";
        jdbcTemplate.update(sql,
                customer.getName(),
                customer.getCustomerId().toString());
    }

    @Override
    public void deleteAll() {
        String sql = "delete from customers";
        jdbcTemplate.update(sql);
    }

    private RowMapper<Customer> customerRowMapper() {
        return (rs, rowNum) -> {
            UUID customerId = UUID.fromString(rs.getString("customer_id"));
            String name = rs.getString("name");
            return new Customer(customerId, name);
        };
    }
}

커넥션을 맺거나 닫아주는 반복적인 작업이 빠지고 훨씬 더 간결해진 것을 확인할 수 있다.

  • jdbcTemplate.update()
    • 데이터를 저장하거나 변경시에 사용한다.
  • jdbcTemplate.queryForObject()
    • 결과 로우가 하나일 때 사용한다.
    • 결과가 없으면 EmptyResultDataAccessException을 던진다.
    • 결과가 두 개 이상이면 IncorrectResultSizedDataAccessException을 던진다.
  • jdbcTemplate.query()
    • 결과가 하나 이상일 때 사용한다.

기존에 JDBC에서 Checked Exception인 SQLExcpetion을 던지던 것과 달리 스프링은 DB 접근 과정에서 발생하는 예외들을 추상화시킨 DataAccessException을 던진다.

'공부방' 카테고리의 다른 글

[TIL 06/30] 스프링 AOP, 스프링 트랜잭션  (0) 2023.07.04
[TIL 06/29] NamedParameterJdbcTemplate, Transaction  (0) 2023.06.30
[TIL 06/27] JDBC  (0) 2023.06.30
[TIL 06/26] 테스트  (0) 2023.06.29
[TIL 06/23] Logging, Logback  (0) 2023.06.23