@Transactional(
propagation = Propagation.REQUIRED, // 1.6. Propagation 전파
isolation = Isolation.REPEATABLE_READ, // 1.7. Isolation Level 격리성 레벨
timeout = -1, // Timeout 트랜잭션 타임아웃
readOnly = false, // 1.8. ReadOnly R 만 허용, CUD 방지
rollbackFor = Exception.class,
noRollbackFor = RuntimeException.class
)
public void method() {
jdbcTemplate.update(...);
jdbcTemplate.update(...);
}
1. Propagation 전파
1.1 물리 / 논리 트랜잭션
분류
내용
(A) 물리 트랜잭션
DB의 트랜잭션 실제 DB의 Connection 통해 시작, 커밋과 롤백 단위
(B) 논리 트랜잭션
Spring의 트랜잭션 PlatformTransactionManager 관리 트랜잭션 단위 / (A)에 연결하여 사용
1.2 Propagation 전파 옵션
@Transactional(propagation = Propagation.REQUIRED)
public void method() {
jdbcTemplate.update(...);
jdbcTemplate.update(...);
}
속성
설명
1) REQUIRED (Default)
- 물리 트랜잭션 존재 시, 논리 트랜잭션으로 해당에 연결 / 없다면 물리 생성 후, 연결 - Single Shared Transaction (양방향) :: 내부 롤백 시 외부 롤백 + 외부 롤백 시 내부 롤백
2) REQUIRES_NEW (독립 트랜잭션)
- 항상 새로운 독립 트랜잭션을 생성 / 각각이 독립되어 있으므로, 커밋-롤백 시점이 구분 - Inner Transaction (단절) :: 내부와 외부는 각자 별도의 롤백/커밋
3) NESTED (중첩/자식 트랜잭션)
- 물리 트랜잭션이 없다면 생성, 이미 있어도 생성(중첩 트랜잭션 생성) - 중첩 트랜잭션 롤백 시 선행 트랜잭션에 전파 X - 선행 트랜잭션 롤백 시 중첩 트랜잭션도 함께 롤백(전파) / 커밋될 때 중첩 트랜잭션도 함께 커밋 - Nested Transaction (단방향) :: 내부 롤백 시 외부 롤백하지 않음 + 외부 롤백 시 내부 롤백
4) SUPPORTS (있으면 써)
- 트랜잭션 존재 X시, 사용 X - 트랜잭션 존재 시, 해당 트랜잭션을 사용
5) NOT_SUPPORTED (안써)
- 트랜잭션을 사용하지 않음 / 있어도 무시
6) MANDATORY (절대 써 기존에있는거)
- 트랜잭션이 존재하지 않는 경우, 예외 발생 - 이미 트랜잭션이 존재하는 경우, 기존 트랜잭션을 그대로 사용
PlatformTransactionManager 안의TransactionSyncrhonizationManager 안에 Connection에 대해
REQUIRED의 경우에는 Connection이 있다면 가져와서 연결 / 없으면 생성 후 연결
REQUIRES_NEW는 Connection의 여부에 상관 없이 새로운 Connection을 생성 후 연결
2. Isolation 격리
출처 : ASAC 수업자료 / 격리 발생 시점
2.1 Isolation Level 옵션
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void method() {
jdbcTemplate.update(...);
jdbcTemplate.update(...);
}
격리 수준
의미
1) DEFAULT
- 하부 데이터베이스의 기본 격리 수준을 이용 (아래 추가 테이블 참조)
Oracle
READ COMMITTED
MySQL
REPEATABLE READ (Inno DB)
MSSQL
READ COMMITTED
PostgresQL
READ COMMITTED
2) READ_UNCOMMITTED
- 커밋X인 데이터 변경사항을 읽기 가능(롤백 되기 전의 값에 대한 읽기 가능) - 가장 빠름 / 데이터의 일관성이 조금 떨어지더라도 성능을 극대화할 때 사용
3) READ_COMMITTED
- 가장 많이 사용되는 격리 수준으로, 다른 트랜잭션에 커밋된 변경사항만 읽기 가능 - 한 트랜잭션 안에서 여러번의 조회가 이뤄짐(= 반복 읽기 문제 :: Non-Repeatable-Read) - 단순한 조회에서 최적화
4) REPEATABLE_READ
- 같은 데이터 필드는 여러 번 반복해서 읽더라도 동일한 값이어야 함 / 조회 가능 - 읽고있는 데이터에 다른 트랜잭션의 수정 제한 / But, 새로운 로우를 추가하는 것은 제한X
5) SERIALIZABLE
- 완전한 ACID를 보장하는 격리 수준 / 동기적으로 실행 - 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 Shared Lock. - 현재 RDBMS는 REPEATABLE_READ에서 MVCC 로 모든 처리가 되어있기에 미사용 격리레벨
3. Read-Only : ORM에서 객체-DB 간 불필요한 동기화 방지
트랜잭션이 시작할 때 읽기 전용 최적화를 수행 : FlushMode 를 NEVER 로 설정
“하이버네이트의 경우 트랜잭션을 읽기 전용으로 선언하는 것은
하이버네이트의 플러시(flush) 모드를 FLUSH_NEVER로 만들어서
하이버네이트로 하여금 객체와 데이터베이스의 불필요한 동기화를 피하고
모든 데이터 갱신을 트랜잭션 말미로 미루게합니다.”
- flush 위한 스냅샷을 만들 필요가 없어 성능 상의 이점
3.1 Read-Only옵션
// Read-Only 설정되어있는 @Transactional 내에서 INSERT, UPDATE, DELETE 수행 시 런타임 오류
@Transactional(readOnly = true)
public void method() {
jdbcTemplate.update(...);
jdbcTemplate.update(...);
}
4. rollbackFor와 noRollbackFor
4.1 rollbackFor
:: 트랜잭션이 롤백될 예외 타입을 지정하는 속성
:: @Transactional은 런타임 예외(unchecked exception)나 Error 발생 시, 트랜잭션을 롤백
:: rollbackFor를 사용하여 특정 예외에 대해서 롤백을 강제로 수행
@Transactional(rollbackFor = { Exception.class })
public void someMethod() {
// 예외가 발생하면 트랜잭션을 롤백합니다.
throw new Exception("예외 발생"); // 런타임 예외 + 지정된 얘외들에 대해 롤백을 진행
}
4.2 noRollbackFor
:: 트랜잭션이 롤백되지 않도록 할 예외 타입을 지정하는 속성
:: RuntimeException 계열의 예외에 대해서는 롤백이 발생
:: But, noRollbackFor를 사용하여 특정 예외에 대해 롤백을 방지 가능
@Transactional(noRollbackFor = { IllegalArgumentException.class })
public void someMethod() {
// IllegalArgumentException이 발생해도 트랜잭션은 롤백 X
throw new IllegalArgumentException("잘못된 인자");
}