[Spring DB 1편] 강의 내용 정리

HJ·2023년 2월 1일
0

Spring DB 1편

목록 보기
7/7

김영한 님의 스프링 DB 1편 - 데이터 접근 핵심 원리 강의를 보고 작성한 내용입니다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard


1. DB 연결

  • DB 마다 연결 방식, SQL 전달 방법 등이 다르다

  ➜ JDBC의 등장으로 어플리케이션 로직이 JDBC 표준 인터페이스에만 의존하면 된다

  • DB에 연결하려면 Connection 이 필요

  ➜ DriverManager.getConnection(URL, USERNAME, PASSWORD)


  • 위의 코드는 커넥션을 새로 생성해서 반환하는 코드이기 때문에 효율적이지 않다

  ➜ 커넥션 풀 사용

  • 어플리케이션 로직은 커넥션이 필요하면 커넥션 풀에 커넥션을 요청

  • 커넥션 풀 사용 방법을 추상화 한 것이 DataSource 인터페이스

    • getConnection() 를 사용해서 커넥션 획득

    • DriverManagerDataSourceHikariDataSource 등이 구현체

    • 각 구현체를 사용하기 위해 필요한 설정이 있는데 이건 2번 게시글 참고

  • DB에 접근하는 Repository 가 DataSource 에 의존한다

    • 외부에서 DataSource 구현체를 주입 받는다

    • Repository 에서 DataSourcegetConnection() 을 사용한다


  • 참고

    • DriverManagerDataSource는 쿼리를 수행할 때마다 커넥션을 새로 생성

    • HikariDataSource는 동일한 커넥션 사용

    • DriverManagerDataSource 는 처음 설정할 때만 필요한 정보를 넘겨주지만 DriverManager 는 커넥션을 생성할 때마다 넘겨주어야 하기 때문에 DriverManagerDataSource를 사용한다




2. 트랜잭션

  • DB 와 커넥션을 맺게 되면 세션이 생성되고, 이 세션을 통해 SQL이 실행

  • 세션이 트랜잭션을 시작하고, 커밋이나 롤백을 통해 트랜잭션을 종료한다

  • 트랜잭션을 시작할 때 수동 커밋 모드로 설정 후 시작한다


  • 트랜잭션은 서비스 계층에서 시작하고 트랜잭션을 시작하려면 커넥션이 필요하다

  ➜ 서비스 계층에서 dataSource.getConnection()으로 커넥션을 생성

  ➜ 트랜잭션 시작을 위해 수동 커밋 모드로 변경한다

  ➜ 커밋이나 롤백 이후 자동 커밋모드로 변경 후 커넥션을 닫는다


  • 같은 세션을 사용하기 위해 동일한 커넥션을 사용해야한다

  ➜ 서비스에서 Repository 의 메서드를 호출할 때 커넥션을 파라미터로 전달

  ➜ Repository 의 메서드에서 커넥션을 닫으면 안된다




3. 스프링과 트랜잭션

3-1. 문제 해결

  • 서비스 계층에서 트랜잭션을 처리를 위해 특정 기술에 의존하는 문제

  ➜ 서비스가 스프링이 제공하는 트랜잭션 추상화 인터페이스에 의존하도록 변경

  ➜ PlatformTransactionManager : 트랜잭션 매니저

  ➜ 각 데이터 접근 기술마다 트랜잭션 매니저 구현체 존재

  ➜ 트랜잭션 매니저는 트랜잭션 추상화 + 리소스 동기화 수행


  • 커넥션을 파라미터로 전달하는 문제

  ➜ 트랜잭션 동기화 매니저 사용

  ➜ 서비스에서 Repository 메서드에 커넥션 전달 X

  ➜ Repository 에서 직접 트랜잭션 동기화 매니저로부터 커넥션 획득


3-2. 동작 방식

  1. 트랜잭션을 시작하려면 커넥션이 필요하기 때문에 트랜잭션 매니저는 데이터 소스를 통해 커넥션을 만들고 트랜잭션을 시작

  2. 트랜잭션 매니저는 트랜잭션이 시작된 커넥션을 트랜잭션 동기화 매니저에 보관

  3. Repository는 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내서 사용 ( 파라미터 전달 X )

  4. 트랜잭션이 종료되면 트랜잭션 매니저트랜잭션 동기화 매니저에 보관된 커넥션을 통해 트랜잭션을 종료하고 커넥션도 닫는다


3-3. 서비스 계층

  • 서비스 계층에서 트랜잭션 매니저 사용

  • PlatformTransactionManager 에 구현체를 DI 받는다

    • 구현체를 주입 받을 때 dataSource 를 받기 때문에 커넥션 획득이 가능하다

    • 획득한 커넥션을 트랜잭션 동기화 매니저에 보관

  • PlatformTransactionManager.getTransaction()

    • 트랜잭션을 시작, 기존에 이미 진행 중인 트랜잭션이 있는 경우 해당 트랜잭션에 참여

    • 커밋이나 롤백 시 필요한 TransactionStatus를 반환

  • 트랜잭션 매니저가 release를 해주기 때문에 직접 하지 않아도 된다

3-4. Repository

  • DataSourceUtils.getConnection(dataSource);

    • 트랜잭션 동기화 매니저가 관리하는 커넥션이 있으면 해당 커넥션을 반환

    • 트랜잭션 동기화 매니저가 관리하는 커넥션이 없는 경우 새로운 커넥션을 생성해서 반환

  • DataSourceUtils.releaseConnection(con, dataSource);

    • 트랜잭션을 사용하기 위해 동기화된 커넥션은 커넥션을 닫지 않고 그대로 유지해준다

    • 트랜잭션 동기화 매니저가 관리하는 커넥션이 없는 경우 해당 커넥션을 닫는다




4. 서비스와 트랜잭션 분리

  • 트랜잭션은 트랜잭션 시작 ➜ 비지니스 로직 수행 ➜ 성공하면 커밋, 실패하면 롤백이 반복

  ➜ 템플릿 콜백 패턴 활용

  ➜ 스프링은 TransactionTemplate 라는 템플릿 클래스를 제공


  • 서비스에서 완전히 트랜잭션 분리

  ➜ 트랜잭션 AOP 활용 ( 4번 게시글 참고 )

  ➜ 트랜잭션 AOP가 트랜잭션 시작, 서비스 호출, 트랜잭션 종료를 수행

  ➜ @Transactional




5. 예외

5-1. 예외 간단 설명

  • 체크 예외의 경우, throws 로 예외를 던져야한다

  ➜ 특정 기술에 의존하게 되는 문제 발생

  ➜ 런타임 예외를 사용

  • 체크 예외를 잡아서 런타임 예외로 변환할 때 기존 예외를 포함해야한다

5-2. 문제 해결

  • 서비스 계층의 Repository 의 구현체에 의존

  ➜ Repository 인터페이스에 의존하도록 변경


  • Repository 의 메서드가 체크 예외를 던지고 있다

  ➜ 체크 예외를 잡아 런타임 예외로 변환한다

  ➜ 변환한 런타임 예외를 던진다




6. DB 접근 예외

  • 해결할 수 있는 예외의 경우 DB 에서 넘겨주는 에러 코드를 확인해서 처리

  ➜ Repository에서 체크 예외를 잡아 런타임 예외로 변환한다

  ➜ 이 때, 예외 코드를 확인해서 상황에 맞는 런타임 예외로 변환

  ➜ 서비스 계층에서 런타임 예외를 잡아 예외 복구 시도


  • BUT> 에러 코드는 DB 마다 다르고, 엄청 많이 정의되어 있다

  ➜ 스프링은 데이터 접근과 관련된 예외를 추상화해서 제공

  ➜ 스프링이 제공하는 예외는 모두 런타임 예외

  ➜ DB 에러 코드를 스프링이 제공하는 예외로 변환해주는 예외 변환기가 있다


  • 예외 변환기

    • SQLExceptionTranslator : 인터페이스

    • SQLErrorCodeSqlExceptionTranslator : 구현체

    • translate() : 오류 코드를 예외로 변환해서 반환

0개의 댓글