본문 바로가기
Back End/Spring

Spring - DB접근4 (JdbcTemplete)

by SolaKim 2023. 7. 4.
  • 순수 Jdbc와 동일한 환경설정을 하면 된다.
  • 스프링 JdbcTemplate과 MyBatis 같은 라이브러리는 JDBC API에서 본 반복 코드를 대부분 제거해준다. 하지만 SQL은 직접 작성해야 한다.

 

package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class JdbcTemplateMemberRepository implements MemberRepository{
    
    private final JdbcTemplate jdbcTemplate;

//  @Autowired //생성자가 한개만 있으면 생략가능
    public JdbcTemplateMemberRepository(DataSource dataSource){
        jdbcTemplate = new JdbcTemplate(dataSource);
    }

    
    @Override
    public Member save(Member member) {
        SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
        jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");

        Map<String, Object> parameters = new HashMap<>();
        parameters.put("name", member.getName());
        
        Number key = jdbcInsert.executeAndReturnKey(new
                MapSqlParameterSource(parameters));
        member.setId(key.longValue());
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
//        return Optional.empty();
//        jdbcTemplate.query("select * from member where id=?", memberRowMapper());
        List<Member> result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(),id);
        return result.stream().findAny();
    }

    @Override
    public List<Member> findAll() {
        return jdbcTemplate.query("select * from member", memberRowMapper());
    }

    @Override
    public Optional<Member> findByName(String name) {
        List<Member> result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(), name);
        return result.stream().findAny();
    }


    private RowMapper<Member> memberRowMapper(){
//        return new RowMapper<Member>() {  => 이걸 람다식으로 바꾼게 밑에꺼
        return (rs, rowNum) -> {
            Member member = new Member();
            member.setId(rs.getLong("id"));
            member.setName(rs.getString("name"));
            return member;
        };
    }

}

 

springconfig 수정

 

이후 스프링 통합 테스트를 실행해보면 오류없이 잘 작동되는것을 알 수 있다.


추가적으로...

JdbcTemplate 사용법

1. JdbcTemplate 설정

스프링에서 JdbcTemplate을 사용하기 위해서는 먼저 jdbc 라이브러리를 프로젝트에 추가해야 한다.

 

Gradle을 기준으로 설명하자면 다음과 같다.

 

//JdbcTemplate 추가
implementation 'org.springframework.boot:spring-boot-starter-jdbc'

build.gradle 파일에 위와 같은 코드를 작성해주면 된다.

JdbcTemplate은 spring-jdbc에 포함되어 있다.

 

 

2. DataSource 주입

private final JdbcTemplate jdbcTemplate;

public JdbcTemplateItemRepository(DataSource dataSource) {
	this.jdbcTemplate = new JdbcTemplate(dataSource);
}

JdbcTemplate은 DataSource를 필요로 한다. DataSource는 스프링 빈으로 등록되어 있어야 한다. 

이렇게 JdbcTemplate을 사용할 때 DataSource를 의존 관계 주입받는 방법과, JdbcTemplate을 스프링 빈으로 직접 등록하고 주입받는 방법이 있지만 전자를 관례상 많이 사용한다.

 

3. 쿼리 작성 및 실행

스프링 JdbcTemplate 사용 방법 공식 매뉴얼이 제공하는 몇 가지 예제를 살펴보자.

 

1) queryForObject()

 

queryForObject() 인터페이스의 정의는 다음과 같다.

<T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException;
  • sql : 실행할 sql문
  • rowMapper : 조회대상 
  • args : 파라미터들

 

이러한 queryForObject() 메서드를 사용하면 단건 조회를 할 수 있다. 이때 조회 대상이 객체가 아닌 단순 데이터라면 타입을 아래와 같이 지정해줄 수 있다.

int rowCount = jdbcTemplate.queryForObject("select count(*) from t_actor",Integer.class);

preparedStatement ("?")를 이용해 파라미터 바인딩도 할 수 있다.? 에 순서대로 파라미터가 들어가게 된다.

int countOfActorsNamedJoe = jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");

 

String을 조회할 때는 아래와 같이 사용할 수도 있다.

String lastName = jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?", String.class, 1212L);

이때 단순 데이터 타입이 아닌 객체를 조회하려면 어떻게 할까?

 

여기서 RowMapper를 활용해 객체를 조회할 수 있다.

 

RowMapper란?

RowMapper는 데이터베이스의 반환 결과인 ResultSet을 객체로 변환해주는 클래스이다. 

 

순수 JDBC를 사용할 때는 객체를 조회할 때 다음과 같이 했다.

 

// 쿼리 날리기
ResultSet rs = stat.excuteQuery("SELECT * FROM Item");

// 결과값 가져오기
while(rs.next()) {
     // item 객체에 값 저장
     item = new Item();
     item.setId(rs.getInt(1));
     item.setItemName(rs.getString(2));
     item.setPrice(rs.getInt(3));
     
     // 리스트에 추가
     itemList.add(item);
}

 

ResultSet의 결과를 개발자가 직접 꺼내 객체에 담아 저장하였다. 

하지만 RowMapper를 사용하면 이러한 반복 작업을 자동화해준다.

 

RowMapper의 mapRow()라는 메서드는 아래와 같이 정의되어 있다.

T mapRow(ResultSet rs, int rowNum) throws SQLException;

ResultSet rs에 결괏값을 담아와 사용자가 원하는 객체에 담는다. rowNum은 반복되는 루프 중 현재 행의 번호를 나타낸다.

 

즉 아래와 같이 동작한다.

public Item mapRow(ResultSet rs, int rowNum) throws SQLException {
    // ResultSet 값을 Item 객체에 저장
    Item item = new Item();
    item.setId(rs.getLong("id"));
    item.setItemName(rs.getString("item_name"));
    item.setPrice(rs.getInt("price"));
    
    // item 반환
    return item;
};

 

이것을 람다식을 이용해 더 간단하게 만들고, queryForObject() 메서드에서 객체를 조회하도록 할 수 있다.

 

String sql = "select id, item_name, price from item where id= ?";

Item item = jdbcTemplate.queryForObject(sql, itemRowMapper(), id);

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

 

2) query() 메서드

 

query() 메서드를 사용하면 여러 건을 조회할 수 있다. 이때도 역시 RowMapper를 이용할 수 있다.

private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> {
    Actor actor = new Actor();
    actor.setFirstName(resultSet.getString("first_name"));
    actor.setLastName(resultSet.getString("last_name"));
    return actor;
};

public List<Actor> findAllActors() {
    return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper);

 

3) update() 메서드

 

INSERT, UPDATE, DELETE 등 데이터를 변경하고 싶을 때는 update() 메서드를 사용할 수 있다. SQL 실행 결과에 영향받은 로우 수를 int로 반환한다.

 

등록

jdbcTemplate.update("insert into t_actor (first_name, last_name) values (?, ?)","Leonor", "Watling");

 

수정

jdbcTemplate.update("update t_actor set last_name = ? where id = ?", "Banjo", 5276L);

 

삭제

jdbcTemplate.update("delete from t_actor where id = ?", Long.valueOf(actorId));

 

 

4) execute() 메서드

 

이외에 임의의 SQL을 실행할 때는 execute() 메서드를 사용할 수 있다. 테이블을 생성하는 DDL 등에 사용할 수 있다.

jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");

 

 


참고

https://code-lab1.tistory.com/277

'Back End > Spring' 카테고리의 다른 글

Spring - DB접근6 (스프링 데이터 JPA)  (0) 2023.07.05
Spring - DB접근5 (JPA)  (0) 2023.07.04
Spring - DB접근3 (스프링 통합 테스트)  (0) 2023.07.04
Spring - DB접근2 (고전 jdbc 이용)  (0) 2023.07.04
Spring - DB접근1 (h2)  (0) 2023.07.04