@Transactional이란?
- 스프링 프레임워크 어노테이션
- 트랜잭션 경계를 정의하는 하나의 작업 단위이다
- 지정된 경계안에서 데이터베이스 작업이 성공하지 않으면 롤백되도록 보장해준다
- 즉, 작업 단위 모두가 성공해야 데이터베이스에 반영한다
- [[스프링이 데이터베이스 레벨의 트랜잭션을 추상화하여 다룬다]]
- 트랜잭션 전파 (Propagation)
- @Transaction(propagtion = Propagation.옵션명)
- 트랜잭션이 어떻게 전파될지를 설정할 수 있다
- 이 말은 트랜잭션이 다른 트랜잭션과 어떻게 연관되는지를 정의하는거다
- 예를 들어서 이미 시작된 트랜잭션이 있을 때 새로운 트랜잭션으로 시작할지 아니면 기존 트랜잭션에 합류할지 등을 설정할 수 있다
- 옵션:
- REQUIRED (기본 설정)
- 이미 트랜잭션이 존재하면 그 트랜잭션에 합류하고 없으면 새로운 트랜잭션을 만들어서 실행한다
- REQUIRES_NEW
- 항상 새로운 트랜잭션을 시작한다
- 이미 트랜잭션이 진행 중이면 기존 트랜잭션을 일시 정지한다
- 별도의 독립적인 트랜잭션이 필요할 때 사용한다
- MANDATORY
- 현재 트랜잭션이 반드시 존재해야 한다
- 트랜잭션이 없으면 예외를 발생시킨다 (IllegalTransactionStateException)
- 그러면 트랜잭션을 하나 시작하면되는거 아닌가?라고 생각했지만 이 옵션은 이 비지니스 로직이 중요하니까 트랜잭션을 반드시 보장 받아야 한다라는 의미로 사용하면 좋을 거 같다.
- NESTED
- 현재 트랜잭션이 존재하면 중첩 트랜잭션을 시작한다
- 중첩 트랜잭션은 기존 트랜잭션을 부모 트랜잭션으로 가진다
- 중첩 트랜잭션 내에서 발생한 롤백은 부모 트랜잭션에 영향을 주지 않는다
- 중첩 트랜잭션 내의 커밋은 부모 트랜잭션의 커밋에 의존한다. 즉, 부모 트랜잭션이 커밋되면 중첩 트랜잭션도 커밋이 된다
- 트랜잭션이 없으면 새로운 트랜잭션을 시작한다
- 트랜잭션 내에서 중첩 트랜잭션을 수행해야 하는 경우 사용한다
- JDBC Savepoints를 사용해서 중첩 트랜잭션을 관리한다
- 언제 필요한가?
- 전체 트랜잭션의 일부 작엄만 롤백하고 싶을 때 사용한다
- 예를 들어, 대량의 데이터 처리 중 특정 부분에서 문제가 발생하면 해당 부분만 롤백하고 나머지는 계속 진행한다
- 현재 트랜잭션이 존재하면 중첩 트랜잭션을 시작한다
- NEVER
- 현재 트랜잭션이 존재하면 예외를 발생시킨다
- 항상 트랜잭션이 없이 실행된다
- NOT_SUPPORTED
- 항상 트랜잭션이 없이 실행된다
- 이미 트랜잭션이 진행 중이면 트랜잭션을 일시 중지한다
- 트랜잭션이 있으면 안되는 경우에 사용한다
- SUPPORTS
- 현재 트랜잭션이 존재하면 그 트랜잭션에 합류하고 없으면 트랜잭션 없이 실행한다
- 트랜잭션이 필수적이지 않을 때 사용한다
- REQUIRED (기본 설정)
- 격리 수준 (Isolation Level)
- @Transaction(isolation = Isolation.옵션명)
- 트랜잭션이 다른 트랜잭션과 어떻게 격리될지 정의한다
- 트랜잭션 격리 수준이란 동시에 실행되는 트랜잭션들이 서로의 중간 상태를 얼마나 볼 수 있는지를 제어하는 설정이다
- 격리 수준은 트랜잭션 간의 간섭을 방지하여 데이터 일관성을 유지하는 데 중요한 역할을 한다
- 격리 수준은 데이터 일관성과 성능 사이의 균형을 조절한다
- 데이터베이스의 격리 수준에 따라 팬텀 리드, 더티 리드, 반복되지 않는 읽기 등의 문제가 발생할 수 있다
- 옵션:
- DEFAULT
- 격리 수준을 별도로 지정하지 않으면, 데이터베이스 벤더의 기본 격리 수준을 사용한다
- oracle, postgreSQL, SQL Server
- READ_COMMITTED
- MySQL
- REPEATABLE_READ
- READ_UNCOMMITTED(읽기 미완료)
- 다른 트랜잭션이 아직 커밋하지 않은 변경 사항을 읽을 수 있다
- 가장 낮은 수준의 격리로 성능이 좋다
- dirty read가 발생할 수 있다
- 예시
- 트랜잭션1가 데이터를 수정하고 아직 커밋을 하지 않은 상태이다
- 트랜잭션2가 트랜잭션1가 수정한 데이터를 읽는다
- 만약 트랜잭션1이 이후 롤백되면, 트랜잭션2가 읽은 데이터는 유효하지 않은 데이터다
- 즉, 데이터 일관성에 악영향이 발생한다
- READ_COMMITTED(읽기 완료)
- 다른 트랜잭션이 커밋한 데이터만 읽을 수 있다
- dirty read가 발생하지 않는다
- non-repeatable read가 발생할 수 있다
- 하나의 트랜잭션이 동일한 데이터를 두 번 이상 읽는 동안, 다른 트랜잭션이 그 데이터를 수정하거나 삭제하여 처음 읽은 데이터와 나중에 읽은 데이터가 달라지는 상황을 의미한다
- REPEATABLE_READ(반복 가능 읽기)
- 트랜잭션 내에서 동일한 데이터를 여러 번 읽어도 항상 동일한 결과를 보장한다
- dirty read, non-repeatable read가 발생하지 않는다
- phantom read가 발생할 수 있다
- 데이터 집합이 달라지는 상황을 의미한다
- 한 트랜잭션이 일정 범위의 데이터를 두 번 이상 읽는 동안 다른 트랜잭션이 그 범위 내에서 새로운 데이터를 삽입하거나 기존 데이터를 삭제하여, 첫 번째 읽기와 두 번째 읽기 사이에 데이터 집합이 달라지는 상황이다
- SERIALIAZABLE
- 가장 높은 수준의 격리
- 트랜잭션이 순차적으로 실행되는 것처럼 보이도록 보장한다
- dirty read, non-repeatable read, phantom read 모두가 발생하지 않는다
- 성능 저하가 발생한다
- DEFAULT
- 시간 제한
- @Transaction(timeout = 30)
- 완료 최대 시간을 설정하고 시간을 초과하면 트랜잭션을 롤백한다
- 읽기 전용
- @Transaction(readonly = true)
- 이 트랜잭션이 데이터의 읽기 전용임을 설정한다
- 데이터 변겨잉 발생하지 않는 트랜잭션에서 성능 최적화를 위해 사용될 수 있다 -> 왜?
- 트랜잭션은 클래스, 메서드 수준으로 사용할 수 있다.
- 클래스에 적용하면 해당 클래스의 모든 메서드에 트랜잭션이 적용된다
- 메서드에 적용하면 해당 메서드 내부 작업의 트랜잭션을 관리할 수 있다
'TIL > Java & Spring Boot' 카테고리의 다른 글
주입할 스프링 빈이 없어도 동작해야 하는 경우 (0) | 2024.06.03 |
---|---|
병렬 스트림 (1) | 2024.06.02 |
람다 표현식 (1) | 2024.06.02 |
Timestamp와 LocalDateTime 타입 차이 (0) | 2023.09.14 |
묵시적 / 명시적 형 변환 (0) | 2022.08.03 |