Spring Transaction

transaction

DB 와 서버가 데이터를 처리하는 과정에서 원자성을 부여하는 기술. 즉, 서버가 DB 에 쿼리를 여러 개 던져서 작업을 처리해야 할 때 관련된 쿼리가 모두 수행되거나 모두 수행 안되게 하는 것을 보장해주는 기술

단일 쿼리로 해결할 수 없는 로직을 처리할 때 필요한 기술이 트랜잭션임. 이것이 가능하려면 하나 이상의 쿼리가 동일한 Connection 객체를 공유해야 가능함.

java.sql.Connection 객체에서 setAutoCommit 을 false 로 하여 사용자가 직접 커넥션에 대해 커밋과 롤백을 해준다는 것을 뜻한다.

spring transaction

스프링의 트랜잭션은 기본적으로 프록시 방식의 구현이다.

구현 방식 설명 제약사항 weaving time
JDK Dynamic Proxy Target Class 의 Interface 가 proxy 역할을 한다. interface 구현체에서만 사용 가능 LTW
CGLib Target Class 의 Subclass 가 proxy 역할을 한다 public method 에서만 사용 가능 LTW
AspectJ Compile Time 에 트랜잭션 기능 구현 private method 에서도 사용 가능 CTW

propagation

  • REQUIRED : 부모 트랜잭션 내에서 실행하며 부모 트랜잭션이 없을 경우 새로운 트랜잭션을 생성
  • REQUIRES_NEW : 부모 트랜잭션을 무시하고 무조건 새로운 트랜잭션이 생성
  • SUPPORT : 부모 트랜잭션 내에서 실행하며 부모 트랜잭션이 없을 경우 nontransactionally로 실행
  • NOT_SUPPORTED : nontransactionally 로 실행하며 부모 트랜잭션 내에서 실행될 경우 일시 정지
  • NEVER : nontransactionally 로 실행되며 부모 트랜잭션이 존재한다면 예외가 발생
  • NESTED : 해당 메서드가 부모 트랜잭션에서 진행될 경우 별개로 커밋되거나 롤백될 수 있음. 둘러싼 트랜잭션이 없을 경우 REQUIRED와 동일하게 작동
  • MANDATORY : 부모 트랜잭션 내에서 실행되며 부모 트랜잭션이 없을 경우 예외가 발생됩

isolation

  • DEFAULT : DB에서 설정된 기본 격리 수준을 따름
  • SERIALIZABLE : 가장 높은 격리수준을 가지며 사용 시 성능 저하가 있을 수 있음
  • READ_UNCOMMITTED : 커밋되지 않은 데이터에 대한 읽기를 허용
  • READ_COMMITTED : 커밋된 트랜잭션에 대해 읽기를 허용
  • REPEATABLE_READ : 동일한 필드에 대한 다중 접근 시 동일한 결과를 얻을 수 잇는 것을 보장

READ_UNCOMMITED 예시

1
2
3
4
5
6
7
8
9
10
11
12
SELECT @@GLOBAL.tx_isolation, @@tx_isolation;

TX A: start transaction;
TX B: set session transaction isolation level read uncommitted;
TX B: start transaction;
TX A: select * from test; -- val = 8
TX B: select * from test; -- val = 8
TX A: update test set val = val + 1; -- val = 9
TX B: select * from test; -- val = 9, dirty read
TX A: rollback;
TX B: select * from test; -- val = 8
TX B: commit;

READ_COMMITED 예시

1
2
3
4
5
6
7
8
9
10
11
SELECT @@GLOBAL.tx_isolation, @@tx_isolation;

TX A: start transaction;
TX B: set session transaction isolation level read committed;
TX B: start transaction;
TX A: select * from test; -- val = 8
TX B: select * from test; -- val = 8
TX A: update test set val = val + 1; -- val = 9
TX B: select * from test; -- val = 8, No dirty read!
TX A: commit
TX B: select * from test; -- val = 9, commited read

REPEATABLE_READ 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT @@GLOBAL.tx_isolation, @@tx_isolation;

TX A: start transaction;
TX B: set session transaction isolation level repeatable read;
TX B: start transaction;
TX A: select * from test; -- val = 8
TX B: select * from test; -- val = 8
TX A: update test set val = val + 1; -- val = 9
TX B: select * from test; -- val = 8
TX A: commit
TX B: select * from test; -- val = 8, repeatable read!
TX B: commit;
TX B: select * from test; -- val = 9 (from tx A)

SERIALIZABLE 예시

1
2
3
4
5
6
7
8
9
10
11
12
SELECT @@GLOBAL.tx_isolation, @@tx_isolation;

TX A: start transaction;
TX B: set session transaction isolation level serializable;
TX B: start transaction;
TX A: select * from test; -- val = 8
TX A: update test set val = val + 1; -- val = 9
TX B: select * from test; -- LOCKED, NO OUTPUT
TX A: commit; -- Unlocked TX B
TX B: select * from test; -- val = 8 (repeatable read!)
TX B: commit;
TX B: select * from test; -- val = 9 (now we see TX A)