| 글의 목적
저는 이전부터 @Transactional의 옵션은 생각하지 못했고, 그저 @Transactional 어노테이션을 추가하면서 사용했습니다. (아.. 그냥 @Transactional 붙이면 되는거 아냐??) 그러므로, 이번 프로젝트를 계기로 @Transactional의 옵션을 정리해보며 좀 더 공부해 볼 수 있는 기회로 삼으면 좋다고 생각해 작성합니다.
옛날의 저처럼, 단순히 @Transactional 어노테이션만 추가하면서 사용해보신 분들에게는 새로운 공부가 될 수 있을 것이라 확신합니다!!! (저도 많이 배웠어요!)
| @Transactional이란?
@Transactional 어노테이션에 대해서 알기 이전에 트랜젝션에 대해서 알아야 합니다.
트랜젝션은 DB의 상태를 변화시키기 위하여 수행을 하는 작업의 단위를 의미합니다. 이는 곧 논리적인 작업 단위이며 정상적일 때는 기대하는 결과를 반환하겠지만, 그렇지 않을 경우에는 롤백이 될 수 있습니다. (지금은 @Transactional이 무엇인지, 그리고 어떠한 옵션을 가지고 있는지가 중심내용이니, 간단하게만 설명하겠습니다.)
즉, @Transactional은 이러한 트랜젝션을 지원하는 어노테이션입니다. 인터페이스, 클래스 혹은 메소드 상단에 @Transactional을 작성할 수 있습니다. (하지만, 추가적으로 인터페이스에 @Transactional을 사용하는 것은 추천하지 않는다고 하네요!)
만약, @Transactional이 작성된 메소드나 클래스가 호출될 경우에는 PlatformTransactionManager에 의해 트랜젝션이 시작되며 트랜젝션이 정상적으로 수행될 경우에는 commit을, 그렇지 않으면 rollback을 하게 됩니다.
| @Transactional의 옵션
| Isolation
Transaction의 격리수준을 설정할 수 있습니다. 이는 곧 여러 트랜젝션이 수행되는 과정에서 다른 트랜젝션에 의해 하나의 트랜젝션이 어느정도 보호되는 지를 설정하는 것입니다.
Dirty Read
트랜잭션이 완료되지 않은 데이터를 볼 수 있는 현상을 의미합니다. 즉, Commit 되지 않은 정보를 볼 수 있습니다.
Phantom Read
트랜잭션 중에 데이터가 삭제되어 이후 조회 시 이전에 존재하던 행이 사라지는 것, 트랜잭션 중에 없던 행이 추가되어 새로 입력된 데이터를 읽는 것을 의미합니다.
Nonrepetable Read
트랜잭션 내에서 한 번 읽은 데이터가 트랜잭션이 커밋되기 전에 변경되었을 경우, 다시 읽었을 때 이전의 결과와 다르게 나타나는 것을 말합니다.
MVCC(Multi-Version Concurrency Control)
동시 접근을 허용하는 데이터베이스에서 동시성을 제어하기 위해 사용하는 방법 중 하나입니다.
자세한 내용
Isolation Level | 설명 |
DEFAULT | - 데이터베이스에 의존합니다. |
READ_UNCOMMITED (Level 0) |
- 가장 낮은 단계의 격리수준입니다. - 처리 중인 데이터를 다른 트랜잭션이 읽는 것을 허용합니다. - Dirty Read, Phantom Read, Repetable Read 모두 발생할 수 있습니다. - DB의 종류에 따라 지원하는 종류가 다릅니다. (Oracle와 Postgre는 지원하지 않습니다.) |
READ_COMMITED (Level 1) |
- 데이터베이스에서 기본적으로 지원하는 Isolation Level입니다. - Commit 되지 않은 데이터를 다른 트랜잭션에서 읽을 수 없습니다. - Dirty Read를 방지하지만, Phantom Read와 Nonrepetable Read는 발생할 수 있습니다. |
REPETABLE_READ (Level 2) |
- 트랜잭션이 완료될 때까지 조회하는 데이터에 Shared Lock이 걸립니다. 그러므로 해당 데이터에 대한 수정이 불가능합니다. - Dirty Read, Nonrepetable Read를 방지하지만, Phantom Read는 발생할 수 있습니다. |
SERIZABLE (Level 3) |
- 최고 수준의 격리이며 동시성 부작용을 방지하지만 그만큼 낮은 속도로 동시 액세스 속도로 이어집니다. - 하나의 트랜젝션이 완료되어야지 다른 트랜젝션이 실행됩니다. - 동일한 데이터에 동시에 여러 트랜젝션이 수행되지 않습니다. - MVCC를 사용하지 않습니다. - Dirty Read, Nonrepetable Read, Phantom Read를 모두 방지합니다. |
| readOnly
트랜잭션에 readOnly 옵션을 부여할 수 있습니다. 만약, 트랜잭션에 readOnly = true을 할 경우, hibernate의 flush mode가 MANUAL로 설정됩니다. MANUAL이 될 경우, 강제로 flush 를 하지 않는 한 flush 가 발생하지 않습니다.
그러므로, 다음의 결과를 얻을 수 있습니다.
1. 트랜잭션을 커밋하더라도 영속성 컨텍스트가 flush가 되지 않아서 엔티티의 등록과 수정, 삭제가 동작하지 않습니다.
2. 읽기 전용으로 영속성 컨텍스트는 변경 감지를 위한 스냅샷을 보관하지 않으므로 성능이 향샹됩니다.
@Transactional(readOnly = true)
| propagation
진행되고 있는 트랜잭션에서 다른 트랜잭션이 호출될 때 어떻게 처리할 지 처리하는 것을 트랜잭션의 전파 설정이라고 합니다. propagation 옵션에 의해 이 설정을 지정할 수 있습니다.
Propagation Level | 설명 |
REQUIRED | - Default Propagation 설정입니다. - 호출된 트랜잭션(부모 트랜잭션)으로 합류합니다. 만약 부모 트랜잭션이 없을 경우에는 새로운 트랜잭션을 생성합니다. - 따라서, 중간에 rollback이 발생한다면 모두 하나의 트랜잭션이기 때문에 진행상황이 모두 rollback됩니다. |
REQUIRES_NEW | - 항상 새로운 트랜잭션을 생성합니다.(부모 트랜잭션 무시) - 각각의 트랜잭션이 롤백되더라도 서로 영향을 주지 않습니다. |
SUPPORT | - 부모 트랜잭션이 존재할 경우 부모 트랜잭션 내에서 실행됩니다. - 만약 부모 트랜잭션이 존재하지 않을 경우 non-transactional로 실행됩니다. |
MANDATORY | - 무조건 부모 트랜잭션에서 실행되며 부모 트랜잭션이 없을 경우에는 예외가 발생합니다. |
NOT_SUPPORTED | - 부모 트랜잭션이 있다면 일시정지시키며 부모 트랜잭션이 없다면 트랜잭션을 생성하지 않습니다. |
NEVER | - 트랜잭션을 생성하지 않습니다. - 부모 트랜잭션이 존재할경우 예외를 발생합니다. |
NESTED | - 부모 트랜잭션이 존재한다면 중첩 트랜잭션을 생성합니다. - 부모 트랜잭션이 존재하지 않는다면 트랜잭션을 생성합니다. - 중첩된 내부의 트랜잭션에서 롤백이 발생할 경우, 중첩된 내부 트랜잭션의 시작 지점까지만 롤백됩니다. - 만약, 커밋을 할경우에는 부모 트랜잭션이 커밋될 때 중첩 트랜잭션도 함께 커밋됩니다. |
| noRollbackFor
특정 예외가 발생하더라도 롤백되지 않도록 설정합니다. 만약 정의한 예외가 트랜잭션 내부에서 발생할 경우, 진행한 부분까지만 commit이 되고 rollback은 진행하지 않습니다.
@Transactional(noRollbackFor = {ExceptionA.class, ExceptionB.class, ExceptionC.class})
public void example(){
}
| rollbackFor
특정 예외가 발생할 경우 롤백을 진행합니다. noRollbackFor와는 반대의 개념입니다. noRollbackFor와 마찬가지로 여러 예외를 지정할 수 있습니다.
| timeout
설정한 값(초)을 넘어가게 되면 트랜잭션 처리가 되지 않고 rollback이 됩니다.
@Transcational(timeout = 10)
public void example(){
}
(참고)
'Develop' 카테고리의 다른 글
주요 Java / MySQL / Spring 버전 정리 (0) | 2022.02.08 |
---|---|
Delete Data : Soft Delete vs Hard Delete (0) | 2021.11.09 |
Deadlock - 교착상태 (0) | 2021.08.24 |