jhhan의 블로그

Spring - JdbcTemplate 본문

Spring

Spring - JdbcTemplate

jhhan000 2020. 10. 17. 17:55

이번 포스트에서는 JdbcTemplate에 대해서 다뤄보겠습니다.

 

JdbcTemplate은 JDBC API에서의 반복 코드를 대부분 제거해 주지만

SQL문은 직접 작성해야 한다는 특징이 있습니다.

JdbcTemplate은 실제로 많이 쓰이기 때문에 알아두면 좋을 것 같습니다.

 

code를 살펴보겠습니다.

먼저 build.gradle 파일입니다.

위의 그림처럼 설정해줍니다.

아마 implementation 'org.springframework.boot:spring-boot-starter-jdbc' 이 부분을 추가하면 되지 않을까 싶습니다.

 

프로젝트 구조입니다.

어쩌면 못 보던 것들이 많이 추가되었을 수도 있지만 이번에 추가할 것은 일단

JdbcTemplateMemberRepository 클래스 입니다. -> 이 자바 클래스를 하나 만들어 주세요

public class JdbcTemplateMemberRepository implements MemberRepository {

    @Override
    public Member save(Member member) {
        return null;
    }

    @Override
    public Optional<Member> findById(Long id) {
        return null;
    }

    @Override
    public Optional<Member> findByName(String name) {
        return null;
    }

    @Override
    public List<Member> findAll() {
        return null;
    }
}

JdbcTemplateMemberRepository 클래스의 기본 구조입니다.

이제 JdbcTemplate을 사용하겠습니다.

public class JdbcTemplateMemberRepository implements MemberRepository {
	
    private final JdbcTemplate jdbcTemplate;

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

    @Override
    public Member save(Member member) {
        return null;
    }

    @Override
    public Optional<Member> findById(Long id) {
        return null;
    }

    @Override
    public Optional<Member> findByName(String name) {
        return null;
    }

    @Override
    public List<Member> findAll() {
        return null;
    }
}

JdbcTemplate은 injection을 받지 않습니다.

대신 DataSource를 Injection 받습니다.

Spring 내에서도 저런 방식으로 사용하는 것을 권장합니다.

※참고 - 생성자가 1개인 경우 @Autowired를 생략해도 됩니다.

위와 같은 경우 @Autowired는 생략이 가능합니다.

생성자가 2개 이상인 경우는 생략하면 안됩니다!!※

 

다음에 이제 각 메소드들을 작성해보겠습니다.

이 메소드들을 작성하기 전에 RowMapper가 필요합니다.

RowMapper는 결과를 받아오기 위해 매핑하는데 사용됩니다.

public class JdbcTemplateMemberRepository implements MemberRepository {
	
    private final JdbcTemplate jdbcTemplate;

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

    @Override
    public Member save(Member member) {
        return null;
    }

    @Override
    public Optional<Member> findById(Long id) {
        return null;
    }

    @Override
    public Optional<Member> findByName(String name) {
        return null;
    }

    @Override
    public List<Member> findAll() {
        return null;
    }
    
    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;
            }
        };
    }
}

RowMapper를 작성합니다.

그러면 아마도 RowMapper를 람다 형식으로 변환할 수 있다는 warning이 뜰 것입니다. 바꿔줍니다!

public class JdbcTemplateMemberRepository implements MemberRepository {
	
    private final JdbcTemplate jdbcTemplate;

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

    @Override
    public Member save(Member member) {
        return null;
    }

    @Override
    public Optional<Member> findById(Long id) {
        return null;
    }

    @Override
    public Optional<Member> findByName(String name) {
        return null;
    }

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

그럼 람다식이 적용된 형태로 변환됩니다.

그럼 이제 쿼리문 3개만 작성해보겠습니다.

public class JdbcTemplateMemberRepository implements MemberRepository {
	
    private final JdbcTemplate jdbcTemplate;

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

    @Override
    public Member save(Member member) {
        return null;
    }

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

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

    @Override
    public List<Member> findAll() {
        return jdbcTemplate.query("select * from member", memberRowMapper());
    }
    
    private RowMapper<Member> memberRowMapper() {
        return (rs, rowNum) -> {
            Member member = new Member();
            member.setId(rs.getLong("id"));
            member.setName(rs.getString("name"));
            return member;
        };
    }
}

다음과 같이 작성을 하면 3개의 메소드를 정상적으로 사용할 수 있습니다.

다음은 save 메소드입니다. save 메소드에서는 새로운 것을 하나 사용해보겠습니다.

    @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());
        // 여기까지가 insert문으로 보면 됨

        Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
        member.setId(key.longValue());
        return member;
    }

SimpleJdbcInsert라는 것을 사용합니다.

Insert문을 작성해서 할 수도 있지만, SimpleJdbcInsert문을 통해서도 Insert를 할 수 있습니다.

  • SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
    -> SimpleJdbcInsert 객체를 하나 생성합니다.
  • jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
    -> 테이블 이름을 설정하고, 자동으로 설정된 키값을 지정해줍니다.
  • 그 다음은 Map을 사용해서 넣어야 할 값들을 차례대로 넣어줍니다.
  • Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
    -> insert문을 실행하고 키값을 리턴받습니다.
    -> MapSqlParameterSource로 Map객체를 받아서 사용합니다.

이런 식으로 하면 나중에 Insert문을 수정할 때 쿼리문을 직접 고치지 않아도 되서 좀 더 수정이 쉬울 것 같습니다.

 

이런 클래스를 하나 만들었으니 이제 SpringConfig 클래스를 변경해줍니다.

이전에 썼던 MemoryMemberRepository 대신 JdbcTemplateMemberRepository로 변경합니다.

그러면 잘 ~ 작동합니다.

 

이전 포스트에서 작성한 테스트 코드가 있는데 그걸 활용해보셔도 됩니다.

현재 사용하고 있는 DB는 H2이고, H2를 실행한 다음 테스트를 해보기 바랍니다.

MemberServiceIntegrationTest를 실행해보시면 됩니다.

그러면

그림과 같이 테스트 실행을 성공적으로 마쳤다는 것을 알 수 있습니다.

코드가 잘 짜졌네요 ㅎㅎ

 

 

 

여기까지 해서 JdbcTemplate에 대해 마치겠습니다.

'Spring' 카테고리의 다른 글

스프링 코어(1) - 스프링 컨테이너 & 빈  (1) 2021.01.04
Spring - AOP  (0) 2020.10.22
Spring - 웹 기능(MVC)  (0) 2020.09.28
Spring - DI(2)  (0) 2020.09.26
Spring - DI(의존성 주입)  (0) 2020.09.20