≣ 목차
1. JdbcTemplate
1-1 JdbcTemplate 소개
JdbcTemplate은 Spring에서 제공하는 클래스로써 JDBC기술을 이용한 데이터베이스 접근을 돕는 클래스입니다. 이 클래스는 반복되는 코드를줄이고, 데이터베이스와의 상호작용을 간소화하여 개발자가 더 쉽게 데이터베이스 작업을 수행할 수 있도록 돕습니다.
1-2 JdbcTemplate의 장단점
장점
- 설정의 편리함
- JdbcTemplate은 spring-jdbc 라이브러리에 포함되어 있는데, 스프링으로 JDBC를 사용할 때 기본으로 사용되는 라이브러리입니다. 그리고 별도의 복잡한 설정 없이 바로 사용할 수 있습니다.
- JDBC기술의 단점 보완
- JdbcTemplate은 JDBC의 반복적인 코드를 줄여 코드의 가독성을 높이고, 예외 처리를 간소화할 수 있습니다.
- 의존성 감소
- 다양한 데이터베이스와의 호환성을 제공하며, 이로 인해 특정 데이터베이스에 대한 의존성을 줄일 수 있습니다.
단점
- 동적 SQL문을 작성하기 매우매우 힘듭니.
- DBMS에 종류에 따라 SQL문법이 다르기에 DBMS에 종속적입니다.
2. NamedParameterJdbcTemplate - 이름 지정 파라미터
NamedParameterJdbcTemplate은 SQL 쿼리에서 이름 지정 파라미터를 사용하여 가독성을 높이고 코드의 유지보수를 용이하게 합니다. 이름 지정 파라미터를 사용하면 파라미터의 순서에 의존하지 않기 때문에, 실수로 파라미터의 순서를 변경하더라도 데이터 바인딩 오류를 방지할 수 있습니다. 이를 통해 코드의 안정성을 높이고, 특히 파라미터가 10개 이상인 복잡한 쿼리에서도 명확한 매핑이 가능합니다. 이름 지정 파라미터를 사용하려면, 파라미터를 Map 형태로 전달해야 하며, 이때 key는 바인딩 네임, value는 해당 바인딩된 값이 됩니다.
2-1 Map - NamedParameterJdbcTemplate
이름 지정 바인딩에서 자주 사용하는 파라미터의 종류는 크게 3가지가 있습니다. 그 중 Map은 key는 바인딩 네임, value는 해당 바인딩된 값을 넣어주시면 됩니다.
public Optional<Item> findById(Long id) {
String sql = "select id, item_name, price, quantity from item where id = :id";
try {
Map<String, Object> param = Map.of("id", id);
Item item = jdbcTemplate.queryForObject(sql, param, itemRowMapper());
return Optional.of(item);
} catch (EmptyResultDataAccessException e) {
return Optional.empty();
}
}
2-2 MapSqlParameterSource
MapSqlParameterSource는 SQL 쿼리에서 바인딩되는 변수를 직접 지정할 수 있는 기능을 제공합니다. 이 클래스는 SqlParameterSource 인터페이스의 구현체로, 메소드 체인을 사용할 수 있는 특징이 있습니다.
public void update(Long itemId, ItemUpdateDto updateParam) {
String sql = "update item set " +
"item_name=:itemName, price=:price, quantity=:quantity " +
"where id=:id";
SqlParameterSource param = new MapSqlParameterSource()
.addValue("itemName", updateParam.getItemName())
.addValue("price", updateParam.getPrice())
.addValue("quantity", updateParam.getQuantity())
.addValue("id", itemId);
jdbcTemplate.update(sql, param);
}
2-3 BeanPropertySqlParameterSource
BeanPropertySqlParameterSource는 자바 빈 프로퍼티 규약을 기반으로 자동으로 SQL 파라미터 객체를 생성하는 기능을 제공합니다.
예를 들어서 getItemName() , getPrice() 가 있으면 다음과 같은 데이터를 자동으로 바인딩합니다.
key=itemName, value=상품명 값
key=price, value=가격 값
이 클래스를 사용하면 객체의 모든 프로퍼티를 손쉽게 SQL 쿼리에 바인딩할 수 있어 코드의 간결성과 가독성을 높입니다. 그러나 BeanPropertySqlParameterSource는 모든 상황에서 사용할 수 있는 것은 아닙니다.
예를 들어 update() 메서드에서는 SQL 쿼리에 :id를 파라미터를 바인딩해야 하지만, 사용하는 DTO에 해당 프로퍼티가 없다면 이 클래스를 사용할 수 없습니다. 이럴 경우, MapSqlParameterSource와 같은 다른 방법을 사용해야 합니다.
public Item save(Item item) {
String sql = "insert into item(item_name, price, quantity) " +
"values(:itemName, :price, :quantity ) ";
KeyHolder keyHolder = new GeneratedKeyHolder();
SqlParameterSource param = new BeanPropertySqlParameterSource(item);
jdbcTemplate.update(sql, param, keyHolder);
long key = keyHolder.getKey().longValue();
item.setId(key);
return item;
}
3. BeanPropertyRowMapper
BeanPropertyRowMapper는 JDBC에서 ResultSet의 결과를 자바 빈 규약에 맞춰 변환하는 기능을 제공합니다.
예를 들어, 데이터베이스에서 select id, price를 조회하면, setId()와 setPrice() 메서드를 호출하여 결과를 매핑합니다. 그러나 se lect item_name과 같이 데이터베이스 컬럼 이름과 자바 빈 프로퍼티 이름이 다를 경우, setItem_name() 메서드가 없어 문제가 발생할 수 있습니다. 이때 SQL 쿼리를 select item_name as itemName으로 수정하여 알리아스를 사용하면 해결할 수 있습니다.
이 방법은 데이터베이스 컬럼 이름과 객체 이름이 완전히 다를 때 유용하며, 예를 들어 member_name을 username으로 매핑할 때도 select member_name as username과 같이 사용할 수 있습니다. 또한, 자바 객체는 카멜 표기법(camelCase)을 사용하고, 관계형 데이터베이스는 주로 언더스코어 표기법(snake_case)을 사용하기 때문에, BeanPropertyRowMapper는 언더스코어 표기법을 카멜 표기법으로 자동 변환하여 select item_name으로 조회해도 setItemName()에 값이 문제 없이 들어갑니다.
요약하자면, snake_case는 자동으로 처리되며, 컬럼 이름과 객체 이름이 완전히 다를 경우에는 SQL에서 알리아스를 사용하면 됩니다.
private RowMapper<Item> itemRowMapper() {
return BeanPropertyRowMapper.newInstance(Item.class);
}
4. SimpleJdbcInsert
JdbcTemplate은 INSERT SQL를 직접 작성하지 않아도 되도록 SimpleJdbcInsert 라는 편리한 기능을 제공합니다.
@Repository
public class JdbcTemplateItemRepository implements ItemRepository {
private final NamedParameterJdbcTemplate jdbcTemplate;
private final SimpleJdbcInsert jdbcInsert;
public JdbcTemplateItemRepositoryV3(DataSource dataSource) {
this.jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
this.jdbcInsert = new SimpleJdbcInsert(dataSource)
.withTableName("item")
.usingGeneratedKeyColumns("id");
}
@Override
public Item save(Item item) {
SqlParameterSource param = new BeanPropertySqlParameterSource(item);
Number key = jdbcInsert.executeAndReturnKey(param);
item.setId(key.longValue());
return item;
}
SimpleJdbcInsert는 생성 시점에 데이터베이스 테이블의 메타데이터를 자동으로 조회합니다. 이 과정에서 테이블의 컬럼 정보를 확인할 수 있으므로, usingColumns 메서드를 생략할 수 있습니다. 즉, 모든 컬럼에 대해 데이터를 삽입할 수 있습니다. 하지만 특정 컬럼만 지정하여 저장하고 싶다면 usingColumns 메서드를 사용하여 원하는 컬럼을 명시할 수 있습니다.
SimpleJdbcInsert 특징
- SimpleJdbcInsert는 생성자에서 DataSource를 받아 의존성 주입을 수행합니다.
- DataSource를 통해 SimpleJdbcInsert를 생성하여 데이터베이스와의 연결을 관리합니다.
- SimpleJdbcInsert를 Spring 빈으로 직접 등록하고 주입받는 방법도 가능합니다.
'DB' 카테고리의 다른 글
[DB] 트랜잭션 정리 2편 - 락(Lock) (0) | 2024.08.08 |
---|---|
[DB] 트랜잭션 정리 1편 (0) | 2024.08.06 |
[DB] SQL Mapper와 ORM 이란? (0) | 2024.08.03 |