본문 바로가기
[inflearn] 스프링 입문

14. 스프링 JdbcTemplate

by 슬픈 야옹이 2023. 4. 30.

JdbcTemplate는 Jdbc를 이용한 DB 연동 프로그램을 좀 더 쉽게 작성하도록 해주는 라이브러리다.

단, SQL은 여전히 직접 작성해야 한다.

 

순수 Jdbc 방식과 달리 JdbcTempalte는 실무에서도 자주 쓰인다.

 

 

 

환경설정

기존 Jdbc와 같은 환경으로 진행한다.

 

// build.gradle에 추가
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'

// application.properties에 추가
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa

 

 

Repository 폴더에 JdbcTemplateMemberRepository 클래스를 생성하여 다음과 같이 작성한다.

JdbcTemplateMemberRepository.java

 

JdbcTemplateMemberRepository.java

package hdxian.hdxianspring.repository;

import hdxian.hdxianspring.domain.Member;
import org.springframework.beans.factory.annotation.Autowired;
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("member2").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) {
        // jdbcTemplate.query()의 리턴 타입은 List<T>
        List<Member> result = jdbcTemplate.query("select * from member2 where id = ?", memberRowMapper(), id);
        return result.stream().findAny();
    }

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

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

    private RowMapper<Member> memberRowMapper() {
        return (rs, rowNum) -> {
            Member member = new Member();
            member.setId(rs.getLong("id"));
            member.setName(rs.getString("name"));
            return member;
        };
    }
}

 

 

 

코드를 간략히 살펴보자.

private final JdbcTemplate jdbcTemplate;

@Autowired
public JdbcTemplateMemberRepository(DataSource dataSource) {
    jdbcTemplate = new JdbcTemplate(dataSource);
}

JdbcTemplate은 JdbcTemplate 타입 객체를 사용한다.

스프링으로부터 DataSource를 인젝션받아 JdbcTemplate 객체를 생성한다.

 

 

@Override
public Member save(Member member) {
    SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
    jdbcInsert.withTableName("member2").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;
 }

데이터 insert는 조금 복잡하다. jdbcTemplate 객체를 이용해 jdbcInsert 객체를 생성하고,

정해진 문법에 따라 데이터를 삽입한다.

JdbcTemplate의 자세한 내용은 따로 학습해야 할 정도로 자세하기 때문에, 지금 단계에서는 대략적인 생김새만 파악한다.

 

 

 

 

@Override
public Optional<Member> findById(Long id) {
    // jdbcTemplate.query()의 리턴 타입은 List<T>
    List<Member> result = jdbcTemplate.query("select * from member2 where id = ?", memberRowMapper(), id);
    return result.stream().findAny();
}

데이터 조회는 간단하다.

jdbcTemplate의 query() 메서드를 이용해 직접 쿼리를 작성하여 DB에 전달한다.

쿼리의 ? 부분에 매핑되는 파라미터는 query() 메서드 뒤의 id 변수다.

 

이전의 순수 Jdbc와는 비교도 안되게 코드가 간단해졌다. 그전까지 직접 작성했던 execute 작성, 예외처리 등을 번거롭게 할 필요가 없어졌다.

 

 

private RowMapper<Member> memberRowMapper() {
    return (rs, rowNum) -> {
        Member member = new Member();
        member.setId(rs.getLong("id"));
        member.setName(rs.getString("name"));
        return member;
    };
}

람다식으로 표기되었지만, 원래는 아래와 같은 형태의 RowMapper<>타입 익명 객체를 리턴하는 형태다.

private RowMapper<Member> memberRowMapper() {
    return new RowMapper<Member>() {
        @Override
        public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
            Member member = new Member();
            member.setId(rs.getLong("id"));
            member.setName(rs.getString("name"));
            return member;
        }
    }
}

쿼리 실행 결과의 각 행을 Member 객체로 매핑해주는 메서드다.

jdbcTemplate.query()에서 이 메서드의 실행 결과를 List<Member> 형태로 리턴한다.

 

 

 

 

작성한 JdbcTemplateMemberRepository가 스프링에서 동작하도록 SpringConfig를 수정한다.

SpringConfig 수정

 

SpringConfig.java

package hdxian.hdxianspring;

import hdxian.hdxianspring.repository.JdbcMemberRepository;
import hdxian.hdxianspring.repository.JdbcTemplateMemberRepository;
import hdxian.hdxianspring.repository.MemberRepository;
import hdxian.hdxianspring.repository.MemoryMemberRepository;
import hdxian.hdxianspring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
public class SpringConfig {

    private final DataSource dataSource;

    @Autowired
    public SpringConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository() {
//        return new MemoryMemberRepository()
//        return new JdbcMemberRepository(dataSource);
        return new JdbcTemplateMemberRepository(dataSource);
    }

}

 

 

동작하는 리포지토리를 바꿨지만, 실제 스프링을 띄워서 확인해볼 필요는 없다.

이전에 통합 테스트 코드를 작성했으므로 간단하게 테스트 가능하다.

통합 테스트

 

 

 

 

 

 

 

 

'[inflearn] 스프링 입문' 카테고리의 다른 글

16. 스프링 데이터 JPA  (0) 2023.04.30
15. 스프링 JPA (Java Persistence API)  (0) 2023.04.30
13. 스프링 통합 테스트  (0) 2023.04.30
12. 순수 JDBC  (0) 2023.04.29
11. H2 데이터베이스 설치  (0) 2023.04.29