본문 바로가기
프로그래밍/java

[Srping] JDBC Template 정리

by 뜨끔쓰 2022. 12. 18.
728x90
728x90
인프런의 김영한님의 강의 스프링 DB 2편 - 데이터 접근 활용 기술을 학습하며 정리한 글입니다.

 

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

 

스프링 DB 2편 - 데이터 접근 활용 기술 - 인프런 | 강의

백엔드 개발에 필요한 DB 데이터 접근 기술을 활용하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., - 강의 소개 | 인

www.inflearn.com

JDBC Template란?

JDBC Template는 꼭 스프링에서만 사용 할 수 있는것은 아니고 의존성만 추가한다면 사용할 수 있는 JDBC를 매우 편리하게 사용 할 수 있게 도와주는 라이브러리다.

 

장점

  • 설정의 편리함
    -> 스프링 사용시 spring-jdbc 라이브러리에 포함되어 있으므로 의존성만 추가한다면 별도의 복잡한 설정 없이 바로 사용가능하다.
  • 반복문제 해결 ->템플릿 콜백 패턴을 사용하여, JDBC를 직접 사용할 때 발생하는 대부분의 반복 작업을 대신처리해줌
    • SQL을 작성하고, 전달할 파라미터와 응답값 매핑만 하면 된다.
    • 커넥션 획득, statement를 준비 & 실행, 결과 반복 루프, 커넥션 종료, statement, resultset 종료
    • 트랜잭션을 다루기 위한 커넥션 동기화
    • 예외 발생시 스프링 예외 변환기 실행

 

단점

  • 동적 SQL 처리가 힘듦

주요 기능

JdbcTemplate이 제공하는 주요 기능은 다음과 같습니다.

  • JdbcTemplate
    • 순서 기반 파라미터 바인딩 지원
  • NamedParameterJdbcTemplate
    • 이름 기반 파라미터 바인딩 지원
  • SimpleJdbcInsert
    • INSERT SQL을 편리하게 사용 가능하도록 지원
  • SimpleJdbcCall
    • 스토어드 프로시저를 편리하게 호출 할수있도록 지원

 

 

이제 사용법을 알아보도록 하자

 

기본적으로 gradle을 사용하여 진행합니다. 

 

DataBase는 H2 DB를 사용합니다.


의존성 추가
	implementation 'org.springframework.boot:spring-boot-starter-jdbc'

한줄만 추가하면 끄읕! (정말?!)

 

사용방법은 CRUD 기반으로 코드를 작성하며 delete부분은 따로 추가하지 않습니다. 

 

각각의 메소드 명은 save, update, findById 정도로 진행합니다.

 

각각의 기능에 사용 될 기본 Item 테이블을 추가하도록 하겠습니다.

 

--테이블 생성

create table item
(
    id        bigint generated by default as identity,
    item_name varchar(10),
    price     integer,
    quantity  integer,
    primary key (id)
);

 

 

 

SAVE(저장)

데이터베이스에 데이터를 저장하는 방법은 여러가지가 존재합니다.

  • JdbcTemplate를 사용하는 방법
  • NamedParameterJdbcTemplate를 사용하는방법
  • SimpleJdbcInsert를 사용하는 방법

 

JdbcTemplate를 사용하는 방법

public class JdbcTemplateItemRepositoryV1 implements ItemRepository {

    private final JdbcTemplate template;

    public JdbcTemplateItemRepositoryV1(DataSource dataSource){
        this.template = new JdbcTemplate(dataSource);
    }

	public Item save(Item item) {
        String sql = "insert into item(item_name, price, quantity) values (?,?,?)";
        KeyHolder keyHolder = new GeneratedKeyHolder();
        template.update(connection -> {
            //자동 증가 키
            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;
    }
}

 

NamedParameterJdbcTemplate를 사용하는 방법

public class JdbcTemplateItemRepositoryV2 implements ItemRepository {

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

 

SimpleJdbcInsert를 사용하는 방법

 

public class JdbcTemplateItemRepositoryV3 implements ItemRepository {

    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");  //생략 가능
    }

    @Override
    public Item save(Item item) {
        SqlParameterSource param = new BeanPropertySqlParameterSource(item);
        Number key = jdbcInsert.executeAndReturnKey(param);
        item.setId(key.longValue());
        return item;
    }
}

 

UPDATE(수정)

update의 경우도 save와 마찬가지로 ?,?,? 형태로 업데이트 컬럼을 작성하여 순서대로 넣는 방식과 각각의 컬럼명(MapSqlParameterSource)을 직접 명시해서 실수를 줄일 수 있는 방법이 있습니다.

 

순서대로 넣는 방법

 

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

    }

 

MapSqlParameterSource 사용하여 수정하는 방법

    public void update(Long itemId, ItemUpdateDto updateParam) {
        String sql = "update item set item_name=:itemName, price=:price, quantity=:quantity " +
                "where id=:itemId";

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

        template.update(sql, param);

    }

 

 

FindById (검색)

검색의 경우 데이터베이스에서 값을 찾아 row을 해당 Class로 매핑해줘야 하는 부분이 필요한데 그부분이 RowMapper인터페이스를 구현해서 처리한다. 

직접 각각의 컬럼을 매핑해주는 방법과 BeanPropertyRowMapper을 이용하는 방법을 알아보도록 하겠습니다.

 

각각의 컬럼을 매핑해주는 방법

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

 

BeanPropertyRowMapper를 이용하고 ?대신 컬럼명으로 where문을 작성하는 방법

    public Optional<Item> findById(Long id) {
        String sql = "select id, item_name, price, quantity from item where id =:id";
        try{
            Map<String, Long> 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 변환 지원
    }

 

기본적인 사용방법을 알아보았는데 개발을 하며 좀 더 자세한 내용이 필요한 부분들은 따로 찾아보며 작업하면 될 것 같습니다.

 

스프링 JdbcTemplate 사용 방법 공식 메뉴얼

https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#jdbc-JdbcTemplate

 

Data Access

The Data Access Object (DAO) support in Spring is aimed at making it easy to work with data access technologies (such as JDBC, Hibernate, or JPA) in a consistent way. This lets you switch between the aforementioned persistence technologies fairly easily, a

docs.spring.io

 

 

728x90
반응형

댓글