DataSource를 사용해보자. => 의존관계 주입 받기\
기존 코드를 유지하기 위해 기존 코드를 복사해서 새로 만들기
- MemberRepositoryV0 -> MemberRepositoryV1
- MemberRepositoryV0Test -> MemberRepositoryV1Test
MemberRepositoryV1과 MemberRepositoryV1Test 두개를 만들어 둔다.
- DataSource 의존관계 주입
- 외부에서 DataSource를 주입 받아서 사용 -> 직접 만든 DBConnectionUtil을 사용하지 않아도 됨
- DataSource는 표준 인터페이스여서 DriveManagerDataSource에서 HikariDataSource로 변경되어도 해당 코드를 변경하지 않아도 됨
- JdbcUtils 편의 메서드
- 스프링은 JDBC를 편리하게 다룰 수 있는 JdbcUtils 라는 편의 메서드를 제공한다
- JdbcUtils 을 사용하면 커넥션을 좀 더 편리하게 닫을 수 있다
MemberRepositoryV1
private Connection getConnection() throws SQLException {
Connection con = dataSource.getConnection();
log.info("get connection={}, class={}", con, con.getClass());
return con;
}
MemberRepositoryV1Test
package hello.jdbc.repository;
import hello.jdbc.domain.Member;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.sql.SQLException;
import java.util.NoSuchElementException;
import static hello.jdbc.connection.ConnectionConst.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@Slf4j
class MemberRepositoryV1Test {
MemberRepositoryV1 repository;
@BeforeEach
void beforeEach() {
// 기본 DriverManager - 항상 새로운 커넥션을 횟ㄱ득
DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
repository = new MemberRepositoryV1(dataSource);
}
@Test
void crud() throws SQLException {
// save
Member member = new Member("memberV100", 10000);
repository.save(member);
// findById
Member findMember = repository.findById(member.getMemberId());
log.info("findMember={}", findMember);
assertThat(findMember).isEqualTo(member);
// update: money: 10000 -> 20000
repository.update(member.getMemberId(), 20000);
Member updatedMember = repository.findById(member.getMemberId());
assertThat(updatedMember.getMoney()).isEqualTo(20000);
// delete
repository.delete(member.getMemberId());
// 검증도 해주면
assertThatThrownBy(() -> repository.findById(member.getMemberId()))
.isInstanceOf(NoSuchElementException.class);
}
}
로그를 보면 계속해서 새로운 커넥션을 볼 수가 있다.
22:11:55.505 [main] DEBUG org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:h2:tcp://localhost/~/test]
22:11:55.677 [main] INFO hello.jdbc.repository.MemberRepositoryV1 - get connection=conn0: url=jdbc:h2:tcp://localhost/~/test user=SA, class=class org.h2.jdbc.JdbcConnection
22:11:55.693 [main] DEBUG org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:h2:tcp://localhost/~/test]
22:11:55.696 [main] INFO hello.jdbc.repository.MemberRepositoryV1 - get connection=conn1: url=jdbc:h2:tcp://localhost/~/test user=SA, class=class org.h2.jdbc.JdbcConnection
22:11:55.700 [main] INFO hello.jdbc.repository.MemberRepositoryV1Test - findMember=Member(memberId=memberV100, money=10000)
22:11:55.782 [main] DEBUG org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:h2:tcp://localhost/~/test]
22:11:55.787 [main] INFO hello.jdbc.repository.MemberRepositoryV1 - get connection=conn2: url=jdbc:h2:tcp://localhost/~/test user=SA, class=class org.h2.jdbc.JdbcConnection
22:11:55.788 [main] INFO hello.jdbc.repository.MemberRepositoryV1 - resultSize=1
22:11:55.789 [main] DEBUG org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:h2:tcp://localhost/~/test]
22:11:55.796 [main] INFO hello.jdbc.repository.MemberRepositoryV1 - get connection=conn3: url=jdbc:h2:tcp://localhost/~/test user=SA, class=class org.h2.jdbc.JdbcConnection
22:11:55.800 [main] DEBUG org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:h2:tcp://localhost/~/test]
22:11:55.805 [main] INFO hello.jdbc.repository.MemberRepositoryV1 - get connection=conn4: url=jdbc:h2:tcp://localhost/~/test user=SA, class=class org.h2.jdbc.JdbcConnection
22:11:55.809 [main] DEBUG org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:h2:tcp://localhost/~/test]
22:11:55.812 [main] INFO hello.jdbc.repository.MemberRepositoryV1 - get connection=conn5: url=jdbc:h2:tcp://localhost/~/test user=SA, class=class org.h2.jdbc.JdbcConnection
커넥션 풀을 쓰도록 코드를 바꿔보자. Sleep를 걸고 돌려보자.
package hello.jdbc.repository;
import com.zaxxer.hikari.HikariDataSource;
import hello.jdbc.domain.Member;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.sql.SQLException;
import java.util.NoSuchElementException;
import static hello.jdbc.connection.ConnectionConst.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@Slf4j
class MemberRepositoryV1Test {
MemberRepositoryV1 repository;
@BeforeEach
void beforeEach() {
// 기본 DriverManager - 항상 새로운 커넥션을 횟ㄱ득
// DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
// 커넥션 풀링
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPoolName(PASSWORD);
repository = new MemberRepositoryV1(dataSource);
}
@Test
void crud() throws SQLException {
// save
Member member = new Member("memberV100", 10000);
repository.save(member);
// findById
Member findMember = repository.findById(member.getMemberId());
log.info("findMember={}", findMember);
assertThat(findMember).isEqualTo(member);
// update: money: 10000 -> 20000
repository.update(member.getMemberId(), 20000);
Member updatedMember = repository.findById(member.getMemberId());
assertThat(updatedMember.getMoney()).isEqualTo(20000);
// delete
repository.delete(member.getMemberId());
// 검증도 해주면
assertThatThrownBy(() -> repository.findById(member.getMemberId()))
.isInstanceOf(NoSuchElementException.class);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
22:19:58.571 [main] INFO hello.jdbc.repository.MemberRepositoryV1 - get connection=HikariProxyConnection@914942811 wrapping conn0: url=jdbc:h2:tcp://localhost/~/test user=SA, class=class com.zaxxer.hikari.pool.HikariProxyConnection
22:19:58.576 [main] INFO hello.jdbc.repository.MemberRepositoryV1 - get connection=HikariProxyConnection@512456259 wrapping conn0: url=jdbc:h2:tcp://localhost/~/test user=SA, class=class com.zaxxer.hikari.pool.HikariProxyConnection
22:19:58.578 [ connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - - Added connection conn7: url=jdbc:h2:tcp://localhost/~/test user=SA
22:19:58.590 [ connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - - Added connection conn8: url=jdbc:h2:tcp://localhost/~/test user=SA
22:19:58.593 [ connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - - Added connection conn9: url=jdbc:h2:tcp://localhost/~/test user=SA
22:19:58.593 [ connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - - After adding stats (total=10, active=0, idle=10, waiting=0)
- DataSource 의존관계 주입
- 외부에서 DataSource를 주입 받아서 사용한다. 직접 만든 DBConnectionUtil을 사용하지 않아도 됨
- DataSource 는 표준 인터페이스 이기 때문에 DriverManagerDataSource 에서
- HikariDataSource 로 변경되어도 해당 코드를 변경하지 않아도 된다
- JdbcUtils 편의 메서드
- 스프링은 JDBC를 편리하게 다룰 수 있는 JdbcUtils 라는 편의 메서드를 제공한다.
- JdbcUtils 을 사용하면 커넥션을 좀 더 편리하게 닫을 수 있다.
DriverManagerDataSource 사용
- DriverManagerDataSource 를 사용하면 conn0~5 번호를 통해서 항상 새로운 커넥션이 생성되어서
사용되는 것을 확인할 수 있다.
get connection=conn0: url=jdbc:h2:.. user=SA class=class
org.h2.jdbc.JdbcConnection
get connection=conn1: url=jdbc:h2:.. user=SA class=class
org.h2.jdbc.JdbcConnection
get connection=conn2: url=jdbc:h2:.. user=SA class=class
org.h2.jdbc.JdbcConnection
get connection=conn3: url=jdbc:h2:.. user=SA class=class
org.h2.jdbc.JdbcConnection
get connection=conn4: url=jdbc:h2:.. user=SA class=class
org.h2.jdbc.JdbcConnection
HikariDataSource 사용
get connection=HikariProxyConnection@xxxxxxxx1 wrapping conn0: url=jdbc:h2:...
user=SA
get connection=HikariProxyConnection@xxxxxxxx2 wrapping conn0: url=jdbc:h2:...
user=SA
get connection=HikariProxyConnection@xxxxxxxx3 wrapping conn0: url=jdbc:h2:...
user=SA
get connection=HikariProxyConnection@xxxxxxxx4 wrapping conn0: url=jdbc:h2:...
user=SA
get connection=HikariProxyConnection@xxxxxxxx5 wrapping conn0: url=jdbc:h2:...
user=SA
get connection=HikariProxyConnection@xxxxxxxx6 wrapping conn0: url=jdbc:h2:...
user=SA
- 커넥션 풀 사용시 conn0 커넥션이 재사용 된 것을 확인할 수 있다.
- 테스트는 순서대로 실행되기 때문에 커넥션을 사용하고 다시 돌려주는 것을 반복한다. 따라서 conn0 만
사용된다
- 웹 애플리케이션에 동시에 여러 요청이 들어오면 여러 쓰레드에서 커넥션 풀의 커넥션을 다양하게 가져가는
상황을 확인할 수 있다.
DI
- DriverManagerDataSource HikariDataSource 로 변경해도 MemberRepositoryV1 의 코드는 전혀
변경하지 않아도 된다. - MemberRepositoryV1 는 DataSource 인터페이스에만 의존하기 때문이다.
이것이 DataSource 를 사용하는 장점이다.(DI + OCP)
<출처 김영한: 스프링 DB 1편 - 데이터 접근 핵심 원리>
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard
스프링 DB 1편 - 데이터 접근 핵심 원리 - 인프런 | 강의
백엔드 개발에 필요한 DB 데이터 접근 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., - 강의
www.inflearn.com
'Spring > SpringDB' 카테고리의 다른 글
트랜잭션 - DB 예제 2 - 자동 커밋 / 수동 커밋 (0) | 2022.06.15 |
---|---|
트랜잭션 - 개념 이해 (0) | 2022.06.15 |
DataSource 예제2 - 커넥션 풀 (0) | 2022.06.14 |
DataSource 예제 - DriverManager (0) | 2022.06.14 |
DataSource 이해 (0) | 2022.06.14 |