Spring/SpringDB

DataSource 적용

느리지만 꾸준하게 2022. 6. 14. 22:38

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