본문 바로가기
Back/DB

[DB] JdbcTemplate

by 오엥?은 2023. 5. 8.
반응형

JdbcTemplate

: SQL을 직접 사용하는 경우에 스프링이 제공하는 JdbcTemplate 은 아주 좋은 선택지다. JdbcTemplate 은 JDBC 를 매우 편리하게 사용할 수 있게 도와준다.

 

• 장점

- 설정의 편리함

 : 별도의 복잡한 설정 없이 바로 사용할 수 있다.

- 반복 문제 해결

 : JdbcTemplate 은 템플릿 콜백 패턴을 사용해서, JDBC 를 직접 사용할 때 발생하는 대부분의 반복 작업을 대신 처리해준다. 개발자는 SQL을 작성하고, 전달할 파라미터를 정의하고, 응답 값을 매핑하기만 하면 된다.

 

• 단점

- 동적 SQL을 해결하기 어렵다.

 

 

 JdbcTemplate 설정하기

• build.gradle 의 dependencies 에 추가해야 할 것

//H2 데이터베이스 추가
runtimeOnly 'com.h2database:h2'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//테스트에서 lombok 사용
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'

 

• application.properties 에 추가해야 할 것

 

ITEM table 만들기

위와 같은 열을 가진 ITEM table 을 만든다.

 

 

JdbcTemplate 적용1

: JdbcTemplate 은 데이터소스(dateSource)가 필요하다.

 

 

① save() : 데이터를 저장한다.

 template.update() : 데이터를 변경할 때는 update() 를 사용하면 된다.

  - INSERT , UPDATE , DELETE SQL에 사용한다.

  - template.update() 의 반환 값은 int 인데, 영향 받은 로우 수를 반환한다.

◽ 데이터를 저장할 때 PK 생성에 identity (auto increment) 방식을 사용하기 때문에, PK인 ID 값을 개발자가 직접 지정하는 것이 아니라 비워두고 저장해야 한다. 그러면 데이터베이스가 PK인 ID를 대신 생성해준다.

 

② update() : 데이터를 업데이트 한다.

template.update() : 데이터를 변경할 때는 update() 를 사용하면 된다.

 - ? 에 바인딩할 파라미터를 순서대로 전달하면 된다.

 - 반환 값은 해당 쿼리의 영향을 받은 로우 수 이다. 여기서는 where id=? 를 지정했기 때문에 영향 받은 로우수는 최대 1개이다.

 

③ findById() : 데이터를 하나 조회한다.

template.queryForObject()

 - 결과 로우가 하나일 때 사용한다.

 - RowMapper 는 데이터베이스의 반환 결과인 ResultSet 을 객체로 변환한다.

 - 결과가 없으면 EmptyResultDataAccessException 예외가 발생한다.

 - 결과가 둘 이상이면 IncorrectResultSizeDataAccessException 예외가 발생한다.

ItemRepository.findById() 인터페이스는 결과가 없을 때 Optional 을 반환해야 한다. 따라서 결과가 없으면 예외를 잡아서 Optional.empty 를 대신 반환하면 된다.

 

④ findAll(): 데이터를 리스트로 조회한다. 그리고 검색 조건으로 적절한 데이터를 찾는다.

template.query()

 - 결과가 하나 이상일 때 사용한다.

 - RowMapper 는 데이터베이스의 반환 결과인 ResultSet 을 객체로 변환한다.

 - 결과가 없으면 빈 컬렉션을 반환한다.

 

 

 JdbcTemplate 적용2 - 동적 쿼리 문제

: 결과를 검색하는 `findAll()` 에서 어려운 부분은 사용자가 검색하는 값에 따라서 실행하는 SQL이 동적으로 달라져야 한다는 점이다.

 

◽ 검색 조건이 없음

select id, item_name, price, quantity from item

◽ 상품명( itemName )으로 검색

select id, item_name, price, quantity from item
where item_name like concat('%',?,'%')

최대 가격( maxPrice )으로 검색

select id, item_name, price, quantity from item
where price <= ?

◽ 상품명( itemName ), 최대 가격( maxPrice ) 둘다 검색

select id, item_name, price, quantity from item
where item_name like concat('%',?,'%')
 and price <= ?

 

 

 JdbcTemplate - 이름 지정 파라미터

① Map

 : 단순히 Map을 사용한다.

Map<String, Object> param = Map.of("id", id);
Item item = template.queryForObject(sql, param, itemRowMapper());

 

② MapSqlParameterSource

 : Map과 유사한데 SQL 타입을 지정할 수 있는 등 SQL 에 좀 더 특화된 기능을 제공한다.

 SqlParameterSource 인터페이스의 구현체이다.

 MapSqlParameterSource 는 메서드 체인을 통해 편리한 사용법도 제공한다.

SqlParameterSource param = new MapSqlParameterSource()
        .addValue("itemName", updateParam.getItemName())
        .addValue("price", updateParam.getItemName())
        .addValue("quantity", updateParam.getQuantity())
        .addValue("id", itemId);

template.update(sql, param);

 

③ BeanPropertySqlParameterSource

: 자바빈 프로퍼티 규약을 통해서 자동으로 파라미터 객체를 생성한다.

예) getXxx() -> xxx() , getItemName -> itemName

예를 들어서 getItemName(), getPrice() 가 있으면 다음과 같은 데이터를 자동으로 만들어낸다.

key=itemName, value=상품명 값

key=price, value=가격 값

SqlParameterSource 인터페이스의 구현체이다.

SqlParameterSource param = new BeanPropertySqlParameterSource(item);
        
KeyHolder keyHolder = new GeneratedKeyHolder();
template.update(sql, param, keyHolder);

여기서 보면 BeanPropertyParameterSource 가 많은 것을 자동화 해주기 때문에 가장 좋아보이지만, 

BeanPropertyParameterSource 를 항상 사용할 수 있는 것은 아니다.

예를 들어서 update() 에서는 SQL 에 :id 를 바인딩 해야 하는데, update() 에서 사용하는 ItemUpdateDto 에는 itemId 가 없다. 따라서 BeanPropertyParameterSource 를 사용할 수 없고, 대신에 MapSqlParameterSource 를 사용했다.

 

BeanPropertyRowMapper

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;
    };
}

이걸 

private RowMapper<Item> itemRowMapper() {
    return BeanPropertyRowMapper.newInstance(Item.class);   // camel 변환 지원
}

이렇게 바꿀 수 있다.

 

 

 

인프런 ) 스프링DB 2편 - 데이터 접근 활용 기술 (김영한)

반응형

'Back > DB' 카테고리의 다른 글

[DB] JPA  (0) 2023.05.12
[DB] MyBatis  (0) 2023.05.09
[DB] H2 데이터베이스 설치  (0) 2023.05.04
[DB] Sharding (샤딩)  (0) 2023.04.27
[DB] Replication (복제)  (0) 2023.04.27