[Spring Boot] Annotation @Transactional

Spring Boot

(Update : 2022-10-31)

Language :

Spring AOP를 기반으로 트랜잭션을 처리하는 선언적 트랜잭션 어노테이션

Transaction 정의

데이터베이스의 데이터/상태를 조작하는 작업 단위

한 번에 수행되어야 하는 연산들

ACID 원칙

원자성 Atomicity

  • 한 트랜잭션 내에서 실행한 작업들은 한 번에 처리한다.
  • 모두 성공하거나 모두 실패한다. 부분적으로 성공하는 일이 없도록 보장한다.

일관성 Consistency

  • 일관성 있는 데이터베이스 상태를 보장한다

격리성 Isolation

  • 동시에 실행되는 트랜잭션들이 서로 영향을 미치지 않게 격리한다.

영속성 Durability

  • 트랜잭션을 성공적으로 마치면 결과를 영구적으로 저장한다.

선언 방법

메소드, 클래스, 인터페이스 위에 선언한다.

적용된 범위에서는 트랜잭션 기능이 포함된 프록시 객체가 생성되어 자동으로 commit/rollback을 실행한다.

프록시 객체에서 접근 가능한 레벨로 지정해야 한다. (private 사용 불가)

트랜잭션은 객체 외부에서 처음 진입하는 메서드를 기준으로 동작한다.

kotlin

@Transactional
fun signUp()

속성 설정

kotlin

@Transactional(propagation = Propagation.REQUIRED)

트랜잭션 전파

트랜잭션 동작 도중에 다른 트랜잭션을 호출할 때 어떻게 할 것인지 설정한다.

  • REQUIRED: 진행 중인 트랜잭션이 있으면 참여하고, 없으면 새로운 트랜잭션을 생성한다.
  • SUPPORTS: 진행 중인 트랜잭션이 있으면 참여하고, 없으면 트랜잭션을 없이 작업을 진행한다.
  • MANDATORY: 진행 중인 트랜잭션이 있으면 참여하고, 없으면 예외를 발생시킨다. 독립적인 작업을 하면 안될 때 사용한다.
  • REQUIRES_NEW: 항상 새로운 트랜잭션을 생성한다. 진행 중인 트랜잭션이 있으면 보류시키고 해당 트랜잭션을 실행시킨다.
  • NOT_SUPPORTED: 트랜잭션 없이 작업을 진행한다. 진행 중인 트랜잭션이 있으면 보류시킨다.
  • NEVER: 트랜잭션 사용 금지. 진행 중인 트랜잭션이 있다면 예외를 발생시킨다.
  • NESTED: 진행 중인 트랜잭션이 있으면 중첩된 트랜잭션을 생성하고, 없으면 새로운 트랜잭션을 생성한다.
  • 중첩된 트랜잭션은 부모 트랜잭션에서 발생하는 커밋과 롤백에 영향을 받지만, 부모 트랜잭션은 중첩된 트랜잭션에 영향을 받지 않는다.

kotlin

@Transactional(isolation = Isolation.DEFAULT)

트랜잭션 격리 수준(Isolation Level)을 설정한다.

트랜잭션에서 일관성이 없는 데이터를 접근할 수 있게 허용하는 수준을 설정한다.

Isolation Level이 낮아질 수록 동시성이 높아지고, 무결성이 낮아져 문제가 발생할 가능성이 커진다.

  • DEFAULT: 기본 격리 수준
  • READ_UNCOMMITTED
  • Level 0
  • SELECT가 수행된 데이터에 Shared Lock이 걸리지 않는다.
  • 변경 사항이 완료(commit)되기 전에 다른 트랜잭션이 해당 행을 읽을 수 있다.
  • A 트랜잭션이 변경하고 완료하지 않은 데이터(Uncommitted or Dirty)를 B 트랜잭션이 읽을 수 있다.
  • Dirty Read, Non-Repeatable Read, Phantom Read 발생 가능
  • READ_COMMITTED
  • Level 1. Read Lock
  • 가장 많이 선택되는 격리 수준. 오라클 DBMS 기본값
  • SELECT가 수행된 행에 Record Lock이 걸려서 변경 사항이 완료되기 전까지 다른 트랜잭션이 읽을 수 없다.
  • A 트랜잭션이 변경한 데이터를 완료하기 전까지 B 트랜잭션이 해당 데이터를 SELECT할 수 없다.
  • B 트랜잭션은 변경 전 데이터(Undo 영역에 백업된 데이터)를 SELECT 한다.
  • MVCC(Multi Version Consurrency Control) 변경 방식을 사용
  • Non-Repeatable Read, Phantom Read 발생 가능
  • REPEATABLE_READ
  • Level 2. Gap Lock
  • MySQL InnoDB Storage Engine 기본값
  • 동일한 SELECT 문장을 수행했을 때 항상 같은 결과를 보장한다. (정합성)
  • A 트랜잭션이 완료되기 전까지 B 트랜잭션은 SELECT, UPDATE, DELETE를 할 수 없다.
  • INSERT는 가능하기 때문에 동일 트랜잭션에서 Read되는 row 수가 달라질 수 있다.
  • Phantom Read 발생 가능하지만 MySQL-InnoDB에서는 MVCC 특성에 의해 발생하지 않음
  • SERIALIZABLE
  • Level 3
  • 가장 단순하면서 가장 엄격한 격리 수준
  • A 트랜잭션이 완료되기 전까지 B 트랜잭션은 어떠한 작업도 수행하지 못한다.
  • DeadLock에 걸리기 쉽다.

kotlin

@Transactional(noRollbackFor = [SafeException::class])

예외 제외. 특정 예외 발생 시 Rollback하지 않는다.

kotlin

@Transactional(rollbackFor = [UserCustomException::class])

예외 추가. 특정 예외 발생 시 Rollback한다.

kotlin

@Transactional(timeout = TransactionDefinition.TIMEOUT_DEFAULT)

트랜잭션 수행 시간을 제한한다. 지정한 시간 내에 작업이 완료되지 않으면 Rollback한다.

default: -1 (no timeout)

kotlin

@Transactional(readOnly = true)

읽기 전용 트랜잭션. 트랜잭션 내에서 데이터를 조작할 수 없다.

true로 설정하면 INSERT, UPDATE, DELETE 실행 시 예외를 발생시킨다.

default: false

REPEATABLE READ Test

REPEATABLE-READ.jpg

Reference

spring framework docs

kdhyo - [Java]@Transactional Annotation 알고 쓰자

TED - @Transactional 동작 원리

AOP와 @Transactional의 동작 원리

민갤

Back-End Developer

백엔드 개발자입니다.