Spring 데이터 억세스
- H2 데이터베이스
- 순수 JDBC
- 스프링 JdbcTemplate : JDBC 중복을 제거해서 만든 템플릿
- JPA : 객체를 쿼리 없이 DB에 저장하는 방법
- 스프링 데이터 JPA : JPA를 간단하게 쓰기 위한 스프링의 Wrapping
H2 데이터베이스
- official site
- bin/h2.sh 실행
- JDBC URL :
jdbc:h2:tcp://localhost/~/h2/test
파일로 직접 접근하지 말고 TCP로 접속.
- build.gradle 추가
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'
JDBC 간단한 정리
Connection
객체 : DB연결 객체PreparedStatement
객체 : 쿼리와 코드를 매칭해준다.ResultSet
객체 : 쿼리 결과를 받는 객체- close() 구현 : DB사용 후 connection을 정리해야한다.
- 스프링에서 JDBC를 붙일때
DataSourceUtils
를 통해 getConnection한다.
JUnit과 AssertJ이 아닌 스프링 통합 테스트
- @SpringBootTest :스프링 컨테이너와 테스트를 함께 실행한다.
- @Transactional : 테스트코드에 붙이면 테스트가 끝나고 나서 작성된 DB데이터를 지운다. 테스트 케이스마다 작동해 다음 테스트에 영향을 미치지 않는다.
- build.gradle 추가
testImplementation 'org.springframework.boot:spring-boot-starter-test'
- 스프링 통합 테스트의 내용
@SpringBootTest
@Transactional
class MemberServiceIntegrationTest {
// 테스트 코드를 작성할때는 필드 주입방법으로 간단하게 진행한다.
@Autowired
MemberService memberService;
@Autowired
MemberRepository memberRepository;
//......
그러면 왜 메모리DB같은 단위 테스트를 하는가?
- 스프링 실행되는 시간만 해도 많은 비용이 발생한다. (테스트코드가 복잡해지면 부지기수로 증가함)
- 스프링 컨테이너 없이 작은 단위에서 실행될 수 있는 테스트가 좋은 테스트.
- 작은 단위의 테스트가 가능하다고 무조건 좋은 테스트는 아니지만 좋은 테스트일 가능성이 높음.
스프링 JdbcTemplate
- JDBC API의 반복코드를 대부분 제거한다.
- 쿼리는 직접 작성해야한다.
- 많이 사용한다.
JdbcTemplate의 선언
public class JdbcTemplateMemberRepository implements MemberRepository {
private final JdbcTemplate jdbcTemplate;
@Autowired
public JdbcTemplateMemberRepository(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
JdbcTemplate의 select 구현
@Override
public Optional<Member> findById(Long id) {
List<Member> result = jdbcTemplate
.query("select * from member where id = ?", memberRowMapper());
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());
}
// memberRowMapper()
private RowMapper<Member> memberRowMapper() {
return (rs, rowNum) -> {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
}
JdbcTemplate의 insert 구현
@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;
}
SimpleJdbcInsert
객체 : 테이블과 칼럼을 잡으면 쿼리를 작성할 필요가 없어진다.