728x90
MemberRepositoryV1
/**
* JDBC - DataSource 사용, JdbcUtils 사용
*/
@Slf4j
public class MemberRepositoryV1 {
private final DataSource dataSource;
public MemberRepositoryV1(DataSource dataSource) {
this.dataSource = dataSource;
}
public Member save(Member member) throws SQLException {
String sql = "insert into member(member_id, money) values (?, ?)";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, member.getMemberId());
pstmt.setInt(2, member.getMoney());
pstmt.executeUpdate(); //쿼리 실행
return member;
} catch (SQLException e) {
log.error("db error", e);
throw e;
} finally {
close(con, pstmt, null);
}
}
public Member findById(String memberId) throws SQLException {
String sql = "select * from member where member_id = ?";
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, memberId);
rs = pstmt.executeQuery();
if (rs.next()) { //최소 한번은 호출해야 데이터가 있는지 없는지 알수 있다
Member member = new Member();
member.setMemberId(rs.getString("member_id"));
member.setMoney(rs.getInt("money"));
return member;
} else {
throw new NoSuchElementException("member not found memberId = " + memberId);
}
} catch (SQLException e) {
log.error("db error", e);
throw e;
} finally {
close(con, pstmt, rs);
}
}
public void update(String memberId, int money) throws SQLException {
String sql = "update member set money = ? where member_id=?";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, money);
pstmt.setString(2, memberId);
int resultSize = pstmt.executeUpdate();//쿼리 실행
log.info("resultSize = {}" , resultSize);
} catch (SQLException e) {
log.error("db error", e);
throw e;
} finally {
close(con, pstmt, null);
}
}
public void delete(String memberId) throws SQLException {
String sql = "delete from member where member_id = ?";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, memberId);
pstmt.executeUpdate();
} catch (SQLException e) {
log.error("db error", e);
throw e;
} finally {
close(con, pstmt, null);
}
}
private Connection getConnection() throws SQLException {
Connection con = dataSource.getConnection();
log.info("get Connection={}, class", con, con.getClass());
return con;
}
private void close(Connection con, Statement stmt, ResultSet rs) {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(stmt);
JdbcUtils.closeConnection(con);
}
}
- DataSource 의존관계 주입
- 외부에서 DataSource를 주입받아서 사용한다. 이제 직접 만든 DBConnectionUtil을 사용하지 않아도 된다.
- DataSource 는 표준 인터페이스 이기 때문에 DriverManagerDataSource에서 HikariDataSource로 변경되어도 해당 코드를 변경하지 않아도 된다.
- JdbcUtils 편의 메서드
- 스프링은 JDBC를 편리하게 다룰 수 있는 JdbcUtils 라는 편의 메서드를 제공한다.
- JdbcUtils 을 사용하면 커넥션을 좀 더 편리하게 닫을 수 있다.
Test
@Slf4j
class MemberRepositoryV1Test {
MemberRepositoryV1 repository;
@BeforeEach
public void beforeEach(){
//기본 DriverManager - 항상 새로운 커낵션을 획득
//DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
//커넥션 풀링
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);
repository = new MemberRepositoryV1(dataSource); //커낵션 풀링 동작
}
@Test
void crud() throws SQLException {
//save
Member member = new Member("memberV0", 10000);
repository.save(member);
//findById
Member findMember = repository.findById(member.getMemberId());
log.info("findMember={}", findMember);
log.info("findMember == member {} ", findMember == member);
log.info("findMember == member {} ", findMember.equals(member));
assertThat(findMember).isEqualTo(member);
//update : money : 10000 -> 20000
repository.update(member.getMemberId(), 20000);
Member updateMember = repository.findById(member.getMemberId());
assertThat(updateMember.getMoney()).isEqualTo(20000);
//좋은 테스트 방식이 아니다
// 테스트 데이터를 지우기 전에 오류가 발생하게 되면 데이터가 삭제되지 않기 떄문에 데이터가 남게 된다.
//delete
repository.delete(member.getMemberId());
assertThatThrownBy(() -> repository.findById(member.getMemberId())).isInstanceOf(
//조회 할 수 없고, 오류가 발생한다.
//반복 테스트가 가능해 졋다
NoSuchElementException.class);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
- MemberRepositoryV1 은 DataSource 의존관계 주입이 필요하다.
- DriverManagerDataSource를 사용하면 항상 새로운 커넥션이 생성되어서 사용되는 것을 확인할 수 있다.
- HikariDataSource를 사용하면
- 커넥션 풀 사용시 conn0 커넥션이 재사용된 것을 확인할 수 있다.
- 테스트는 순서대로 실행되기 때문에 커넥션을 사용하고 다시 돌려주는 것을 반복한다. 따라서 conn0 만 사용된다.
- 웹 애플리케이션에 동시에 여러 요청이 들어오면 여러 쓰레드에서 커넥션 풀의 커넥션을 다양하게 가져가는 상황을 확인할 수 있다.
DI
DriverManagerDataSource -> HikariDataSource 로 변경해도 MemberRepositoryV1의 코드는 전혀 변경하지 않아도 된다.
MemberRepositoryV1 는 DataSource 인터페이스에만 의존하기 때문이다. 이것이 DataSource를 사용하는 장점이다.(DI + OCP)
728x90
'스프링 DB 1편(데이터 접근 핵심 원리)' 카테고리의 다른 글
| Ch03. 트랜잭션 이해 - 트랜잭션(DB 예제 & 자동 커밋, 수동 커밋) (0) | 2022.04.25 |
|---|---|
| Ch03. 트랜잭션 이해 - 트랜잭션(개념 이해) (0) | 2022.04.25 |
| Ch02. 커넥션풀과 데이터소스 이해 - DataSource 예제 (0) | 2022.04.24 |
| Ch02. 커넥션풀과 데이터소스 이해 - DataSource 이해 (0) | 2022.04.24 |
| Ch02. 커넥션풀과 데이터소스 이해 - 커넥션 풀 이해 (0) | 2022.04.24 |