김영한 님의 스프링 DB 1편 - 데이터 접근 핵심 원리 강의를 보고 작성한 내용입니다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard
➜ JDBC의 등장으로 어플리케이션 로직이 JDBC 표준 인터페이스에만 의존하면 된다
➜ DriverManager.getConnection(URL, USERNAME, PASSWORD)
➜ 커넥션 풀 사용
커넥션 풀 사용 방법을 추상화 한 것이 DataSource
인터페이스
getConnection()
를 사용해서 커넥션 획득
DriverManagerDataSource
와 HikariDataSource
등이 구현체
각 구현체를 사용하기 위해 필요한 설정이 있는데 이건 2번 게시글 참고
DB에 접근하는 Repository 가 DataSource
에 의존한다
외부에서 DataSource
구현체를 주입 받는다
Repository 에서 DataSource
의 getConnection()
을 사용한다
참고
DriverManagerDataSource
는 쿼리를 수행할 때마다 커넥션을 새로 생성
HikariDataSource
는 동일한 커넥션 사용
DriverManagerDataSource
는 처음 설정할 때만 필요한 정보를 넘겨주지만 DriverManager
는 커넥션을 생성할 때마다 넘겨주어야 하기 때문에 DriverManagerDataSource
를 사용한다
DB 와 커넥션을 맺게 되면 세션이 생성되고, 이 세션을 통해 SQL이 실행
세션이 트랜잭션을 시작하고, 커밋이나 롤백을 통해 트랜잭션을 종료한다
트랜잭션을 시작할 때 수동 커밋 모드로 설정 후 시작한다
➜ 서비스 계층에서 dataSource.getConnection()
으로 커넥션을 생성
➜ 트랜잭션 시작을 위해 수동 커밋 모드로 변경한다
➜ 커밋이나 롤백 이후 자동 커밋모드로 변경 후 커넥션을 닫는다
➜ 서비스에서 Repository 의 메서드를 호출할 때 커넥션을 파라미터로 전달
➜ Repository 의 메서드에서 커넥션을 닫으면 안된다
➜ 서비스가 스프링이 제공하는 트랜잭션 추상화 인터페이스에 의존하도록 변경
➜ PlatformTransactionManager
: 트랜잭션 매니저
➜ 각 데이터 접근 기술마다 트랜잭션 매니저 구현체 존재
➜ 트랜잭션 매니저는 트랜잭션 추상화 + 리소스 동기화 수행
➜ 트랜잭션 동기화 매니저 사용
➜ 서비스에서 Repository 메서드에 커넥션 전달 X
➜ Repository 에서 직접 트랜잭션 동기화 매니저로부터 커넥션 획득
트랜잭션을 시작하려면 커넥션이 필요하기 때문에 트랜잭션 매니저는 데이터 소스를 통해 커넥션을 만들고 트랜잭션을 시작
트랜잭션 매니저는 트랜잭션이 시작된 커넥션을 트랜잭션 동기화 매니저에 보관
Repository는 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내서 사용 ( 파라미터 전달 X )
트랜잭션이 종료되면 트랜잭션 매니저는 트랜잭션 동기화 매니저에 보관된 커넥션을 통해 트랜잭션을 종료하고 커넥션도 닫는다
서비스 계층에서 트랜잭션 매니저 사용
PlatformTransactionManager
에 구현체를 DI 받는다
구현체를 주입 받을 때 dataSource 를 받기 때문에 커넥션 획득이 가능하다
획득한 커넥션을 트랜잭션 동기화 매니저에 보관
PlatformTransactionManager.getTransaction()
트랜잭션을 시작, 기존에 이미 진행 중인 트랜잭션이 있는 경우 해당 트랜잭션에 참여
커밋이나 롤백 시 필요한 TransactionStatus를 반환
DataSourceUtils.getConnection(dataSource);
트랜잭션 동기화 매니저가 관리하는 커넥션이 있으면 해당 커넥션을 반환
트랜잭션 동기화 매니저가 관리하는 커넥션이 없는 경우 새로운 커넥션을 생성해서 반환
DataSourceUtils.releaseConnection(con, dataSource);
트랜잭션을 사용하기 위해 동기화된 커넥션은 커넥션을 닫지 않고 그대로 유지해준다
트랜잭션 동기화 매니저가 관리하는 커넥션이 없는 경우 해당 커넥션을 닫는다
➜ 템플릿 콜백 패턴 활용
➜ 스프링은 TransactionTemplate
라는 템플릿 클래스를 제공
➜ 트랜잭션 AOP 활용 ( 4번 게시글 참고 )
➜ 트랜잭션 AOP가 트랜잭션 시작, 서비스 호출, 트랜잭션 종료를 수행
➜ @Transactional
throws
로 예외를 던져야한다➜ 특정 기술에 의존하게 되는 문제 발생
➜ 런타임 예외를 사용
➜ Repository 인터페이스에 의존하도록 변경
➜ 체크 예외를 잡아 런타임 예외로 변환한다
➜ 변환한 런타임 예외를 던진다
➜ Repository에서 체크 예외를 잡아 런타임 예외로 변환한다
➜ 이 때, 예외 코드를 확인해서 상황에 맞는 런타임 예외로 변환
➜ 서비스 계층에서 런타임 예외를 잡아 예외 복구 시도
➜ 스프링은 데이터 접근과 관련된 예외를 추상화해서 제공
➜ 스프링이 제공하는 예외는 모두 런타임 예외
➜ DB 에러 코드를 스프링이 제공하는 예외로 변환해주는 예외 변환기가 있다
예외 변환기
SQLExceptionTranslator
: 인터페이스
SQLErrorCodeSqlExceptionTranslator
: 구현체
translate()
: 오류 코드를 예외로 변환해서 반환