일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- Java
- design pattern
- redis
- IT
- C
- c언어
- Algorithm
- 운영체제
- Proxy
- 파이썬
- react
- MSA
- spring webflux
- Spring
- Kafka
- Galera Cluster
- Heap
- 자료구조
- MySQL
- 컴퓨터구조
- OS
- 네트워크
- JPA
- Data Structure
- 알고리즘
- 디자인 패턴
- JavaScript
- 자바
- 백준
- mongoDB
Archives
- Today
- Total
시냅스
Spring JdbcTemplate 정리 본문
JdbcTemplate
- JdbcTemplate은 JDBC를 편리하게 사용할 수 있는 SQL Mapper이다.
- JdbcTemplate은 spring-jdbc 라이브러리에 포함되어 있어 복잡한 설정 없이 스프링으로 JDBC를 사용할 때 기본으로 사용되는 라이브러리이다.
- 콜백 패턴(RowMapper)을 사용하여 반복잡업을 대신해준다.
- 개발자는 SQL을 작성하고, 전달할 파라미터를 정의하고, 응답 값을 매핑하기만 하면 된다.
- 아래는 JdbcTemplate이 대신 처리해주는 작업들이다.
- 커넥션 획득
- statement를 준비하고 실행
- 결과를 반복하도록 루프 실행
- 커넥션 종료, statement, resultset 종료
- 트랜잭션 다루기 위한 커넥션 동기화
- 예외 발생시 스프링 예외 변환기 실행
- 단점으로는 동적 SQL을 해결하기 어렵다는 점이 있다.
용례 - insert
@Repository
public class repository {
private final JdbcTemplate template;
public JdbcTemplateItemRepositoryV1(DataSource dataSource) {
this.template = new JdbcTemplate(dataSource); // datasource를 주입받아 할당한다.
}
public Item save(Item item) {
String sql = "insert into item(item_name, price, quantity) values (?, ?, ?)";
// db로부터 키 값을 부여 받는다.
KeyHolder keyHolder = new GeneratedKeyHolder();
template.update(connection -> {
//자동 증가 키 지정 -> new String[]{"id"}
PreparedStatement ps = connection.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;
}
}
- 가장 기본적인 방법으로 개발자가 직업 binding 하는 작업이 이뤄지고 있다.
public class Repository {
private final NamedParameterJdbcTemplate template;
public JdbcTemplateItemRepositoryV2(DataSource dataSource) {
this.template = new NamedParameterJdbcTemplate(dataSource);
}
@Override
public Item save(Item item) {
String sql = "insert into item(item_name, price, quantity) values (:itemName, :price, :quantity)";
SqlParameterSource param = new BeanPropertySqlParameterSource(item);
KeyHolder keyHolder = new GeneratedKeyHolder();
template.update(sql, param, keyHolder);
long key = keyHolder.getKey().longValue();
item.setId(key);
return item;
}
}
- Sql에서 ? 로 바인딩하는 대신에 :파라미터이름 을 통해 바인딩이 이뤄지고 있다.
- BeanPropertySqlParameterSource에서 바인딩이 이뤄진다.
- 이 외에도 SimpleJdbcInsert 를 통해 insert문을 작성하지 않고도 insert를 할 수 있는 방법도 있다.
용례 - update
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);
}
- 각 ? 에 변수들을 바인딩해준다.
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);
template.update(sql, param);
}
- 위의 template은 NamedParameterJdbcTemplate이다.
- MapSqlParameter를 통해 각 컬럼들을 binding해준다.
용례 - 단건 조회
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();
}
}
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;
});
}
- queryForObejct 를 통해 데이터를 조회한다.
- 이때 RowMapper를 사용하는데, RowMapper는 ResultSet을 객체로 변환한다.
- 만약 결과가 없다면 EmptyResultDataAccessException이 발생한다.
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 = template.queryForObject(sql, param, itemRowMapper());
return Optional.of(item);
} catch (EmptyResultDataAccessException e) {
return Optional.empty();
}
}
private RowMapper<Item> itemRowMapper() {
return BeanPropertyRowMapper.newInstance(Item.class); // camel 변환 지원
}
- 위의 template은 NamedParameterJdbcTemplate이다.
- BeanPropertyRowMapper를 통해 결과를 객체로 변환한다.
용례 - 다건 조회
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());
}
- template.query() 는 결과가 하나 이상일 때 사용한다.
- 마찬가지로 RowMapper를 통해 결과를 객체로 변환한다.
- 쿼리에 대한 조건들과 sql을 동적으로 변환한다.
public List<Item> findAll(ItemSearchCond cond) {
String itemName = cond.getItemName();
Integer maxPrice = cond.getMaxPrice();
SqlParameterSource param = new BeanPropertySqlParameterSource(cond);
String sql = "select id, item_name, price, quantity from item";
if (StringUtils.hasText(itemName) || maxPrice != null) {
sql += " where";
}
boolean andFlag = false;
if (StringUtils.hasText(itemName)) {
sql += " item_name like concat('%',:itemName,'%')";
andFlag = true;
}
if (maxPrice != null) {
if (andFlag) {
sql += " and";
}
sql += " price <= :maxPrice";
}
log.info("sql={}", sql);
return template.query(sql, param, itemRowMapper());
}
- 위의 template은 NamedParameterJdbcTemplate이다.
- 위와 마찬가지로 동적으로 할당하는데, 다만 바인딩 문법이 달라졌다.
정리
- JdbcTemplate
- 순서 기반 파라미터 바인딩
- NamedParameterJdbcTemplate
- 이름 기반 파라미터 바인딩을 지원
- SimpleJdbcInsert
- INSERT SQL을 편리하게 사용할 수 있다.
- SimpleJdbcCall
- 스토어드 프로시저를 편리하게 호출
'Java, Spring' 카테고리의 다른 글
Spring 선언적 트랜잭션과 AOP, 프록시 객체에 대한 이해 (0) | 2022.09.03 |
---|---|
Spring MyBatis 정리 (0) | 2022.09.03 |
Java 예외 관련 정리 (0) | 2022.08.25 |
Java 트랜잭션 관련 정리 (0) | 2022.08.25 |
Java 커넥션 풀과 데이터 소스 (0) | 2022.08.25 |
Comments