Published 2024. 2. 28. 16:23

스프링은 @Transactional을 통해 트랜잭션을 관리한다.

스프링은 트랜잭션 처리를 TransactionManager를 통해서 처리한다. (인터페이스 PlatformTransactionManagere 사용)

 

사용법 1

TransactionTemplate : 트랜잭션을 세부적으로 걸기 위해 설정한다.

- 스프링 IoC를 통해 설정 영역과 DB실행 코드가 분리되었다.

- db커넥션을 개발자가 관리하지 않는다. 개발자는 트랜잭션 콜백 메서드만 구현한다.

- 저수준 SQLException을 스프링에서 잡아 추상화된 런타임 에외로 포장한다.

 

@Transactional은 dynamic proxy의 특징 (클래스 혹은 메서드 단위로만 사용해야하기 떄문에 세부적인 처리가 어렵다.)

만약 한 트랜잭션에 (주문 접수 + 이메일 발송) 로직이 있는데, 이메일 발송이 안된다고 트랜잭션에 의해 롤백되면 안된다.

 

사용법 2

@Transactional

기본적으로 객체에 @EnableTransactionManagement가 필요하다. => Spring @AutoConfiguration에 기본 포함되었다.

(EnableTransactionManagement기능으로 어디에 트랜잭션이 있는지 확인한다. 컴포넌트 스캔의 대상이 아님!)

 

1. 스프링은 @Transactional 어노테이션 메서드를 가진 메서드를 발견하면, 다이나믹 프록시를 만든다. (BeanPostProcessor) 

-> 프록시로 만드는 이유 :  메서드 호출을 가로채 추가 로직을 실행하기 위해서

2. 이 프록시 객체는 TransactionManager 객체에게 트랜잭션 동작을 위임하는 코드를 만든다. (AOP를 사용, 프록시 자체가 트랜잭션 코드를 실행하는게 아니다)

3. 트랜잭션 매니저는 jdbc코드를 통해 트랜잭션을 실행한다. 결국 실제 동작은 jdbc로 처리된다. (doBegin, doCommit)

4.데이터베이스 연결을 준비한다. DataSourceUtils.prepareConnectionForTransaction  (트랜잭션격리 레벨 등 설정)

DataSource 설정으로 DB 연결 => setAutoConnit(false) 트랜잭션 커밋의 제어권을 내가 가짐 -> 커밋하는 시점을 내가 정할 수 있어 요구사항에 따라 동작 범위에 대응할 수 있음

5. 트랜잭션 동기화 매니저를 사용해 스레드 로컬을 사용해 Connection을 동기화한다. => 스레드 로컬은 멀티 스레드 상황에 안전하게 커넥션을 동기화하게 해줌

6. Repository(데이터 접근로직)은 트랜잭션 동기화 매니저에 보관된 커넥션을 사용한다. (커넥션을 따로 전달하지 않아도 됨)

7. 트랜잭션이 종료되는 시점에서 트랜잭션 매니저는 트랜잭션 동기화 매니저에 보관된 커넥션을 통해 트랜잭션을 종료하고 커넥션을 닫음

 

@Transactional(readOnly=true) 동작

jdbc드라이버는 읽기 전용 상태를 mysql로 전달하려고 시도한다.

jpa의 경우 readonly true 설정을 하면 flush를 동작하지 않는다. 물론 더티체킹도 하지 않는다. 

하지만, save update와 같은 요청을 쿼리까지 생성되게 된다. 하지만 내부 로직을 보면, readonly설정이 mysql에 반영되어 있어 insert나 update쿼리는 반영되지 않는다.

 

 

조회를 위해선 걸어줘야만 하는 (readOnly=true)  이유

동시에 다른 트랜잭션에서 데이터 변경이 일어나면, 읽기 작업중인 트랜잭션은, 변경전 데이터를 읽거나, 변경중인 데이터를 읽는 문제가 발생할 수 있다. 결국 데이터의 일관성을 유지시키기 위해서 성능을 향상시키는 도구이다.

 

@Async 를 같이 사용하는 경우

비동기  요청을 사용한다는건 새로운 스레드로 트랜잭션을 사용한다는 의미이다. 

이전에도 보았듯이 ThreadSyncronizationManager는 ThreadLocal로 관리한다.

그렇기 때문에 @Async인 다른 스레드로 요청이 오면, 호출한 메서드와 서로 다른 스레드에서 동작하기 때문에 트랜잭션을 공유할 수 없다. 

전파레벨로 치면 Required로 오면 부모 트랜잭션과 별개인 새로운 트랜잭션이 생성되고 둘은 연관되지 않는다. Mandatory의 경우에는 예외를 터뜨린다. 

 

 

 

 

https://jiwondev.tistory.com/154#head11

https://juno-juno.tistory.com/93

https://jiwondev.tistory.com/154#head11

복사했습니다!