[SpringDB2] JdbcTemplate1
JdbcTemplate 소개
JdbcTemplate은 JDBC를 매우 편리하게 사용할 수 있게 도와준다.
장점
- 설정의 편리함
- JdbcTemplate은
spring-jdbc
라이브러리에 포함되어 있는데, 이 라이브러리는 스프링으로 JDBC를 사용할 때 기본으로 사용되는 라이브러리이다. 그리고 별도의 복잡한 설정 없이 바로 사용할 수 있다.
- JdbcTemplate은
- 반복 문제 해결
- JdbcTemplate은 템플릿 콜백 패턴을 사용해서, JDBC를 직접 사용할 때 발생하는 대부분의 반복 작업을 대신 처리해준다.
- 개발자는 SQL을 작성하고, 전달할 파라미터를 정의하고, 응답 값을 매핑하기만 하면 된다.
- 대부분의 반복 작업을 대신 처리해준다.
- 커넥션 획득
statement
를 준비하고 실행- 결과를 반복하도록 루프를 실행
- 커넥션 종료,
statement
,resultset
종료 - 트랜잭션 다루기 위한 커넥션 동기화
- 예외 발생시 스프링 예외 변환기 실행
단점
- 동적 SQL을 해결하기 어렵다.
적용1 - 기본
메모리에 저장하던 데이터 -> 데이터베이스에 저장
Repository
JdbcTemplateItemRepositoryV1
/**
* JdbcTemplate
*/
@Slf4j
@Repository
public class JdbcTemplateItemRepositoryV1 implements ItemRepository {
private final JdbcTemplate template;
public JdbcTemplateItemRepositoryV1(DataSource dataSource) {
this.template = new JdbcTemplate(dataSource);
}
@Override
public Item save(Item item) {
String sql = "insert into item (item_name, price, quantity) values (?, ?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
template.update(con -> {
// 자동 증가 키
PreparedStatement ps = con.prepareStatement(sql, new String[]{"id"});
ps.setString(1, item.getItemName());
ps.setInt(2, item.getPrice());
ps.setInt(3, item.getQuantity());
return ps;
}, keyHolder);
long key = keyHolder.getKey().longValue();
item.setId(key);
return item;
}
@Override
public void update(Long itemId, ItemUpdateDto updateParam) {
String sql = "update item set item_name=?, price=?, quantity=? where id=?"
template.update(sql,
updateParam.getItemName(),
updateParam.getPrice(),
updateParam.getQuantity(),
itemId);
}
@Override
public Optional<Item> findById(Long id) {
String sql = "select id, item_name, price, quantity from item where id = ?";
try {
Item item = template.queryForObject(sql, itemRowMapper(), id);
return Optional.of(item);
} catch (EmptyResultDataAccessException e) {
return Optional.empty();
}
}
@Override
public List<Item> findAll(ItemSearchCond cond) {
String itemName = cond.getItemName();
Integer maxPrice = cond.getMaxPrice();
String sql = "select id, item_name, price, quantity from item";
//동적 쿼리
if (StringUtils.hasText(itemName) || maxPrice != null) {
sql += " where";
}
boolean andFlag = false;
List<Object> param = new ArrayList<>();
if (StringUtils.hasText(itemName)) {
sql += " item_name like concat('%',?,'%')";
param.add(itemName);
andFlag = true;
}
if (maxPrice != null) {
if (andFlag) {
sql += " and";
}
sql += " price <= ?";
param.add(maxPrice);
}
log.info("sql={}", sql);
return template.query(sql, itemRowMapper(), param.toArray());
}
private RowMapper<Item> itemRowMapper() {
return (rs, rowNum) -> {
Item item = new Item();
item.setId(rs.getLong("id"));
item.setItemName(rs.getString("item_name"));
item.setPrice(rs.getInt("price"));
item.setQuantity(rs.getInt("quantity"));
return item;
};
}
}
JdbcTemplate
은dataSource
가 필요하다.
save()
template.update()
INSERT
,UPDATE
,DELETE
SQL에 사용한다.
- 데이터를 저장할 때 PK 생성에
identity
(auto increment) 방식을 사용하기 때문에, PK인 ID 값을 개발자가 직접 지정하는 것이 아니라 비워두고 저장해야 한다. - 이렇게 데이터베이스가 대신 생성해주는 PK ID 값은, 데이터베이스에 INSERT가 완료 되어야 생성된 PK ID 값을 확인할 수 있다.
KeyHolder
와connection.prepareStatement(sql, new String[]{"id"})
를 사용해서id
를 지정해주면INSERT
쿼리 실행 이후에 데이터베이스에서 생성된 ID 값을 조회할 수 있다.- 참고로, JdbcTemplate이 제공하는
SimpleJdbcInsert
라는 편리한 기능이 있으므로 대략 이렇게 사용한다 정도로 알아만 두면 된다.
findById()
template.queryForObject()
- 결과 로우가 하나일 때 사용.
RowMapper
는 데이터베이스의 반환 결과인ResultSet
을 객체로 변환한다.- 결과가 없으면
EmptyResultDataAccessException
예외가 발생한다. - 결과가 둘 이상이면
IncorrectResultSizeDataAccessException
예외가 발생한다.
ItemRepository.findById()
인터페이스는 결과가 없을 때Optional
을 반환해야 한다. 따라서, 결과가 없으면 예외를 잡아서Optional.empty
를 대신 반환하면 된다.
findAll()
template.query()
- 결과가 하나 이상일 때 사용
RowMapper
는 데이터베이스의 반환 결과인ResultSet
을 객체로 변환한다.- 결과가 없으면 빈 컬렉션을 반환한다.
itemRowMapper()
데이터베이스의 조회 결과를 객체로 변환할 때 사용.
JdbcTemplate이 다음과 같은 루프를 돌려주고, 개발자는 RowMapper
를 구현해서 그 내부 코드만 채운다고 이해하면 된다.
while(resultSet 이 끝날 때 까지) {
rowMapper(rs, rowNum)
}
적용2 - 동적 쿼리 문제
결과를 검색하는 findAll()
에서 어려운 부분은 사용자가 검색하는 값에 따라서 실행하는 SQL이 동적으로 달라져야 한다는 점이다.
// 검색 조건 X
select id, item_name, price, quantity from item
// 상품명으로 검색
select id, item_name, price, quantity from item
where item_name like concat('%',?,'%')
// 최대 가격으로 검색
select id, item_name, price, quantity from item
where price <= ?
// 상품명, 최대 가격 둘 다 검색
select id, item_name, price, quantity from item
where item_name like concat('%',?,'%')
and price <= ?
결과적으로 4가지 상황에 따른 SQL을 동적으로 생성해야 한다.
적용3 - 구성과 실행
JdbcTemplateV1Config
@Configuration
@RequiredArgsConstructor
public class JdbcTemplateV1Config {
private final DataSource dataSource;
@Bean
public ItemService itemService() {
return new ItemServiceV1(itemRepository());
}
@Bean
public ItemRepository itemRepository() {
return new JdbcTemplateItemRepositoryV1(dataSource);
}
}
ItemServiceApplication - 변경
//@Import(MemoryConfig.class)
@Import(JdbcTemplateV1Config.class)
@SpringBootApplication(scanBasePackages = "hello.itemservice.web")
public class ItemServiceApplication {}
댓글남기기