스프링 Jdbc Template
오늘은 Jdbc template에 관해 학습하였다. 기본적으로 Jdbc template은 순수한 Jdbc 코드에서의 반복 코드를 제거해준다. 그러나, SQL은 직접 작성해야한다. Jdbc template은 실제로 실무에서도 많이 사용한다고 하셨다. 그만큼 복잡했던 Jdbc를 편리하게 작성할 수 있는 장점 때문인 것 같다. 본격적으로 Jdbc template 을 생성해보자.
먼저, 다음과 같이 repository package에 JdbcTemplateMemberRepository
class를 생성한다.
JdbcTemplateMemberRepository.class
package hello.hellospring.repository;
import hello.hellospring.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; // Jdbc Template이 따로 있다.
@Autowired // 1.
public JdbcTemplateMemberRepository(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override // 2.
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 // 3.
public Optional<Member> findById(Long Id) {
List<Member> result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(), Id);
return result.stream().findAny();
}
@Override // 4.
public Optional<Member> findByName(String name) {
List<Member> result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(), name);
return Optional.empty();
}
@Override // 5.
public List<Member> findAll() {
return jdbcTemplate.query("select * from member", memberRowMapper());
}
// 6.
private RowMapper<Member> memberRowMapper() {
return (rs, rowNum) -> {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
}
}
주석처리로 넘버링한 코드의 설명은 다음과 같다.
DataSource
로 Injection을 받음. 그 후, JdbcTemplate
객체를 생성하고, dataSource
를 받도록 해줌. (위와 같이 생성자가 하나만 있으면 @Autowired 생략가능)RowMapper
라는 메소드로 매핑해주는 작업이 필요. result는 List로 받아서 Optional로 반환Id
를 name
으로 바꿔서 구현RowMapper
를 람다식으로 구현한 메소드. 이제 조립(?)하는 단계만 남았다. SpringConfig
class를 살펴보자.
SpringConfig.class
@Bean
public MemberRepository memberRepository() {
// return new MemoryMemberRepository();
// return new JdbcMemberRepository(dataSource);
return new JdbcTemplateMemberRepository(dataSource);
}
위와 같이 memberRepository
메소드에 JdbcTemplateMemberRepository
를 dataSource
를 입력인자로 받고 리턴하게끔 하면 된다.
그리고 실행시켜보자. 우리는 저번에 스프링 통합 테스트를 만들어놓았다. 이제 DB에 내용이 반영되었는지 웹을 통해 검증할 필요없이 심플하게 그것만 실행시켜서 테스트를 진행하면 된다.
(테스트가 통과되는 모습)
강사님께서 테스트 코드의 중요성에 대해서 설명해주셨다. 현업에서도 프로덕션 코드를 개발하는 것보다 오히려 테스트 코드를 개발하는 비중이 더 많은데 이는 테스트코드의 중요성을 말해주고 있다. 개발을 잘하는 개발자일수록 테스트코드를 정교하고 꼼꼼하게 잘 구성한다고 말씀하셨다. 테스트를 통해 원천적인 오류를 찾아내고 해결하는 능력을 갖추는 것이 좋은 개발자의 자질임을 항상 기억해야겠다.