H2 데이터베이스 설치
순수 JDBC
스프링 통합 테스트
https://www.h2database.com
h2 데이터베이스 버전은 스프링 부트 버전에 맞춘다.
h2.bat
실행h2/bin/h2.bat
실행
최초 실행 시 데이터베이스 파일을 생성해야 한다.
JDBC URL: jdbc:h2:~/test
를 입력하고 연결 클릭
C:\Users\jung
에 test.mv.db
파일 생성 확인
이후부터는 JDBC URL: jdbc:h2:tcp://localhost/~/test
로 연결한다.
파일에 직접 접근하지 않고 소켓을 통해 접근하는 것.
데이터베이스가 꼬이면 서버 연결을 완전히 끊고,
test.mv.db
삭제하고 다시 생성하는 것도 방법이다.
DDL(Data Definition Language)를 h2 콘솔에 입력하여 데이터베이스를 생성한다.
drop table if exists member CASCADE;
create table member
(
id bigint generated by default as identity,
name varchar(255),
primary key (id)
);
Long
이 sql 문법에서는 bigint
generated by default as identity
: db가 자동으로 값 채워줌DML(Data Manipulation Language)를 h2 콘솔에 입력하여 데이터베이스에 데이터를 삽입한다.
insert into member(name) values('spring')
프로젝트 루트에 sql
디렉토리 생성 후,
ddl.sql
에 위의 ddl문을 저장해두면 github에 업로드할 수 있고 관리도 편하다.
JdbcMemberRepository
작성package com.jiu.spring_basic.repository;
import com.jiu.spring_basic.domain.Member;
import org.springframework.jdbc.datasource.DataSourceUtils;
import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class JdbcMemberRepository implements MemberRepository {
private final DataSource dataSource;
public JdbcMemberRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Member save(Member member) {
String sql = "insert into member(name) values(?)";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
pstmt.setString(1, member.getName());
pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys(); // Statement.RETURN_GENERATED_KEYS 와 매칭
if (rs.next()) {
member.setId(rs.getLong(1));
} else {
throw new SQLException("id 조회 실패");
}
return member;
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
@Override
public Optional<Member> findById(Long id) {
String sql = "select * from member where id = ?";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, id);
rs = pstmt.executeQuery();
if(rs.next()) {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return Optional.of(member);
} else {
return Optional.empty();
}
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
@Override
public List<Member> findAll() {
String sql = "select * from member";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
List<Member> members = new ArrayList<>();
while(rs.next()) {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
members.add(member);
}
return members;
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
@Override
public Optional<Member> findByName(String name) {
String sql = "select * from member where name = ?";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name);
rs = pstmt.executeQuery();
if(rs.next()) {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return Optional.of(member);
}
return Optional.empty();
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
private Connection getConnection() {
return DataSourceUtils.getConnection(dataSource); // DataSourceUtils를 통해서 getConnection
}
private void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null) {
close(conn);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
private void close(Connection conn) throws SQLException {
DataSourceUtils.releaseConnection(conn, dataSource); // DataSourceUtils를 통해서 releaseConnection
}
}
SpringConfig
변경@Configuration
public class SpringConfig {
private DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MemberRepository memberRepository() {
// return new MemoryMemberRepository();
return new JdbcMemberRepository(dataSource);
}
}
DataSource
꼭 h2 database를 실행시킨 후, spring application을 실행시켜야 한다.
http://localhost:8080 에 접속하면 h2 데이터베이스에 생성했던 회워 목록을 조회할 수 있다.
회원 가입을 하면, 가입된 회원을 h2 콘솔에서도 확인할 수 있다.
기존 테스트는 순수 자바 테스트코드.
스프링 컨테이너와 DB까지 연결한 통합 테스트를 진행해보자.
MemberServiceIntegrationTest
@SpringBootTest
@Transactional // 테스트 후 rollback
class MemberServiceIntegrationTest {
// test 시 간단하게 field injection
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@Test
void join() {
//given
Member member = new Member();
member.setName("spring");
//when
Long saveId = memberService.join(member);
//then
Member findMember = memberService.findOne(saveId).get();
assertThat(member.getName()).isEqualTo(findMember.getName());
}
@Test
void Dup_Member_join(){
//given
Member member1 = new Member();
member1.setName("dup");
Member member2 = new Member();
member2.setName("dup");
//when
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
}
@SpringBootTest
@Transactional
delete from member