JdbcTemplate 소개와 설정
SQL을 직접 사용하는 경우 스프링이 제공하는 JdbcTemplate을 선택하면 좋음
(+) JdbcTemplate은 spring-jdbc 라이브러리에 포함되어 있는데, 이 라이브러리는 스프링으로 JDBC를 사용할 때 기본으로 사용되는 라이브러리로, 별도 복잡한 설정 없이 바로 사용이 가능함
(+) JdbcTemplte은 템플릿 콜백 패턴을 사용해 JDBC를 직접 사용할 때 발생하는 반복 작업을 대신 처리해줌
- 커넥션 획득
- statement 준비 및 실행
- 결과를 반복하도록 루프 실행
- 커넥션 종료, statement, resultset 종료
- 트랜잭션을 다루기 위한 커넥션 동기화
- 예외 발생시 스프링 예외 변환기 실행
(+) 개발자는 SQL을 작성하고, 전달 파라미터를 정의하고, 응답 값을 매핑하기만 하면 됨
(-) 동적 SQL을 해결하기 어려움
JdbcTemplate 적용
JdbcTemplateRepositoryV1.java
JdbcTemplate은 dataSource가 필요함 : dataSource를 의존 관계 주입 받고 생성자 내부에서 JdbcTemplate 생성
(JdbcTemplate을 스프링 빈으로 직접 등록 후 주입받아도 됨)
private final JdbcTemplate template;
public JdbcTemplateItemRepositoryV1(DataSource dataSource) {
this.template = new JdbcTemplate(dataSource);
}
save()
@Override
public Item save(Item item) {
String sql = "insert into item (item_name, price, quantity) values (?, ?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
template.update(connection -> {
{"id"});
//자동 증가 키
PreparedStatement ps = connection.prepareStatement(sql, new String[]
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;
}
update()
@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);
}
findById()
@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();
}
}
findAll()
@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;
};
}
-> template.query() : 결과가 하나 이상일 때 사용
-> RowMapper는 데이터베이스의 반환 결과인 ResultSet을 객체로 변환함
-> 실무에서는 더 복잡한 동적 쿼리가 필요한데 이는 너무 복잡함.. MyBatis는 이런 동적 쿼리를 쉽게 작성할 수 있게 도와줌
JdbcTemplate - 이름 지정 파라미터
JdbcTemplateItemRepositoryV2.java
NamedParameterJdbcTemplate 사용 : 파라미터를 이름을 지정해서 바인딩 함
private final NamedParameterJdbcTemplate template;
public JdbcTemplateItemRepositoryV2(DataSource dataSource) {
this.template = new NamedParameterJdbcTemplate(dataSource);
}
save()
@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;
}
이름 지정 바인딩에서 자주 사용하는 파라미터 종류는 크게 3가지가 있다.
1. Map 사용
Map<String, Object> param = Map.of("id", id);
Item item = template.queryForObject(sql, param, itemRowMapper());
2. SqlParameterSource > MapSqlParameterSource
: 메서드 체인을 통해 편리한 사용
SqlParameterSource param = new MapSqlParameterSource()
.addValue("itemName", updateParam.getItemName())
.addValue("price", updateParam.getPrice())
.addValue("quantity", updateParam.getQuantity())
.addValue("id", itemId); //이 부분이 별도로 필요하다.
template.update(sql, param);
3. SqlParameterSource > BeanPropertySqlParameterSource
: 자바빈 프로퍼티 규약을 통해 자동으로 파라미터 객체 생성
SqlParameterSource param = new BeanPropertySqlParameterSource(item);
KeyHolder keyHolder = new GeneratedKeyHolder();
template.update(sql, param, keyHolder);
BeanPropertyRowMapper 사용
: 기존 V1의 RowMapper 함수를 다음과 같이 변경 가능
private RowMapper<Item> itemRowMapper() {
return BeanPropertyRowMapper.newInstance(Item.class); //camel 변환 지원
}
-> ResultSet의 결과를 받아 자바빈 규약에 맞춰 데이터 변환
-> 언더스코어 표기법을 카멜로 자동으로 변환해줌
JdbcTemplate - SimpleJdbcInsert
JdbcTemplateItemRepositoryV3.java
SimpleJdbcInsert : Insert SQL을 직접 작성하지 않아도 됨
private final NamedParameterJdbcTemplate template;
private final SimpleJdbcInsert jdbcInsert;
public JdbcTemplateItemRepositoryV3(DataSource dataSource) {
this.template = new NamedParameterJdbcTemplate(dataSource);
this.jdbcInsert = new SimpleJdbcInsert(dataSource)
.withTableName("item")
.usingGeneratedKeyColumns("id");
.usingColumns("item_name", "price", "quantity"); //생략 가능
}
-> withTableName : 데이터 저장할 테이블 명
-> usingGenerateKeyColumns : key를 생성하는 PK 컬럼 명 지정
-> usingColumns : INSERT SQL에 사용할 컬럼 지정
save()
@Override
public Item save(Item item) {
SqlParameterSource param = new BeanPropertySqlParameterSource(item);
Number key = jdbcInsert.executeAndReturnKey(param);
item.setId(key.longValue());
return item;
}
정리
JdbcTemplate의 최대 단점은 동적 쿼리 문제를 해결하지 못한다는 것과, SQL을 자바로 작성하기 때문에 SQL 라인이 코드를 넘어갈 때 마다 문자 더하기를 해주어야 한다는 것이다.
-> 이 문제를 해결할 수 있는 기술이 MyBatis이다.
'인프런 > 스프링 DB 2편 - 데이터 접근 활용 기술' 카테고리의 다른 글
| [인프런] 스프링 DB 2편 - 데이터 접근 기술 활용 / 6. 데이터 접근 기술 - 스프링 데이터 JPA (0) | 2024.08.11 |
|---|---|
| [인프런] 스프링 DB 2편 - 데이터 접근 기술 활용 / 5. JPA (0) | 2024.08.04 |
| [인프런] 스프링 DB 2편 - 데이터 접근 기술 활용 / 4. MyBatis (1) | 2024.07.28 |
| [인프런] 스프링 DB 2편 - 데이터 접근 기술 활용 / 3. 데이터 접근 기술 - 테스트 (2) | 2024.07.22 |
| [인프런] 스프링 DB 2편 - 데이터 접근 기술 활용 / 1. 데이터 접근 기술 - 시작 (0) | 2024.07.03 |