스프링 트랜잭션
Isolation
SQL 의 Isolation level 과 동일하게 동작 (SQL 격리수준 속성에 대한 자세한 내용은 이곳을 참고)
READ_UNCOMMITED : commit 되지 않은 데이터를 읽는다
READ_COMMITED : commit 된 데이터만 읽는다
REPEATABLE_READ : 자신의 트랜잭션이 생성되기 이전의 트랜잭션(낮은 번호의 트랜잭션)의 커밋된 데이터만 읽는다
SERIALIZABLE : LOCK 을 걸고 사용
DEFAULT : 사용하는 DB 기본 설정을 따른다. (Oracle 은 READ_COMMITED, Mysql InnoDB 는 REPEATABLE_READ 가 Default)
스프링 트랜잭션 전파 타입
Propagation
트랜잭션이 시작하거나 참여하는 방법에 관한 설정
트랜잭션의 경계에서 트랜잭션이 어떻게 동작할 것인가
public class ServiceA {
private ServiceB serviceB;
...
@Transactional
public void a() {
service.b();
}
}
public class ServiceB {
@Transactional
public void b() {...}
}
위의 코드에서처럼 트랜잭션이 처리되는 과정안에서 또 다른 트랜잭션이 처리되는 경우가 있다.
부모 트랜잭션이 있냐 없냐에 따라 타입별로 트랜잭션의 경계를 설정할 수 있다.
@Transactional을 클래스 또는 메서드 레벨에 명시하면 해당 메서드 호출시 지정된 트랜잭션이 작동하게 된다. 해당 클래스의 Bean이 다른 클래스의 Bean에서 호출될 때만 @Transactional을 인지하고 작동하게 된다.
(같은 Bean 내에서 @Transactional이 명시된 다른 메서드를 호출해도 작동하지 않는다.)
스프링 프레임워크는 내부적으로 AOP를 통해서 해당 애노테이션을 인지하여 프록시를 생성해서 트랜잭션을 자동 관리한다.
전파 타입 7가지 요약
REQUIRE : 부모 트랜잭션(자신을 호출한 메소드의 Transaction)이 존재한다면 그에 포함되어 동작. 부모 트랜잭션이 존재하지 않을 경우 새로 트랜잭션 생성(default 속성)
SUPPORTS : 부모 트랜잭션이 존재하면 그에 포함되어 동작. 부모 트랜잭션이 없다면 트랜잭션 없이 동작.
MANDATORY : 부모 트랜잭션이 존재하면 그에 포함되어 동작. 부모 트랜잭션이 없다면 예외 발생시킨다.
REQUIRES_NEW : 부모 트랜잭션 존재 여부와 상관없이 트랜잭션을 새로 생성
NOT_SUPPORTED : 트랜잭션을 사용하지 않는다. 부모 트랜잭션이 존재하면 보류시킨다.
NEVER : 트랜잭션을 사용하지 않도록 강제한다. 부모 트랜잭션이 존재할 경우 예외를 발생시킨다.
NESTED : 부모 트랜잭션이 존재하면 부모 트랜잭션 안에 트랜잭션을 만든다. 부모트랜잭션의 커밋과 롤백에 영향을 받지만 자신의 커밋과 롤백은 부모 트랜잭션에 영향을 주지 않는다.
전파 타입 7가지 상세
Propagation.REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
public void doSomething() { ... }
특정 메서드의 트랜잭션이 Propagation.REQUIRED로 설정된 경우 동작방식
기본적으로 해당 메서드를 호출한 곳에서 별도의 트랜잭션이 설정되어있지 않았다면, 트랜잭션을 새로시작! (새로운 연결을 생성하고 실행)
만약 호출한 곳에서 이미 트랜잭션이 설정되어있다면, 기존의 트랜잭션 내에서 로직 실행 (동일한 연결 안에서 실행)
예외가 발생하면 롤백이 되고, 호출한 곳에도 롤백이 전파된다.
기본값이므로 생략 가능
만약 해당 메서드가 호출한 곳과 별도의 스레드라면?
답은 전파레벨과 상관없이 무조건 별도의 트랜잭션을 생성하여 해당 메서드를 실행
스프링은 내부적으로 트랜잭션의 정보를 ThreadLocal 변수에 저장하기 때문에 다른 스레드로 트랜잭션이 전파되지 않는다!!
Propagation.REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSomething() { ... }
- Propagation.REQUIRES_NEW의 경우
- 매번 새로운 트랜잭션을 시작한다. (새로운 연결(DB 커넥션)을 생성하고 실행한다.)
- 만약 호출한 곳에서 이미 트랜잭션이 설정되어있다면(기존의 연결이 존재한다면), 기존의 트랜잭션은 메서드가 종료할 때까지 잠시 대기 상태로 두고 자신의 트랜잭션을 실행한다.
- 새로운 트랜잭션 안에서 예외가 발생해도, 호출한 곳에는 롤백이 전파되지 않는다.
- 즉 두개의 트랜잭션은 완전히 독립적인 별개의 단위로 작동한다.
Propagation.NESTED
@Transactional(propagation = Propagation.NESTED)
public void doSomething() { ... }
- Propagation.NESTED의 경우
- 기본적으로 앞서 설명한 Propagation.REQUIRED와 동일하게 작동
- 중요한 차이점은 SAVEPOINT를 지정한 시점까지 부분 롤백이 가능하다는 것! (아직 커밋되지 않은 상태에서 SAVEPOINT를 지정할 수 있고 해당 포인트까지 롤백이 가능한 DB의 기능)
- 유의할 점은 데이터베이스가 SAVEPOINT 기능을 지원해야한다. (대표적으로 ORACLE)
- 이미 진행중인 트랜잭션이 있다면 중첩 트랜잭션을 시작
- 해당 메소드가 부모 트랜잭션에서 진행될 경우 별개로 커밋되거나 롤백될 수 있다.
- 둘러싼 부모 트랜잭션이 없을 경우 Propagation.REQUIRED와 동일하게 동작
Propagation.MANDATORY
@Transactional(propagation = Propagation.MANDATORY)
public void doSomeThing() {...}
- 부모 트랜잭션 내에서 실행되며, 부모 트랜잭션이 없을 경우 Exception 발생
Propagation.SUPPORT
@Transactional(propagation = Propagation.SUPPORT)
public void doSomeThing() {...}
- 부모 트랜잭션이 존재하면 부모 트랜잭션으로 동작하고, 없을 경우 non-transactional하게 동작
Propagation.NOT_SUPPORT
@Transactional(propagation = Propagation.NOT_SUPPORT)
public void doSomeThing() {...}
- non-transactinal로 실행되며, 부모 트랜잭션이 존재하면 부모 트랜잭션을 일시정지하고 수행한다.
Propagation.NEVER
@Transactional(propagation = Propagation.NEVER)
public void doSomeThing() {...}
- non-transactinal로 실행되며, 부모 트랜잭션이 존재하면 Exception이 발생
추가
- no-rollback-for : 롤백하지 않을 익셉션 타입
- rollback-for : 롤백할 익셉션 타입
참고
'CS > DB' 카테고리의 다른 글
인덱스 (0) | 2021.12.28 |
---|---|
정규화 (0) | 2021.12.28 |
트랜잭션 격리수준 (0) | 2021.12.28 |
관계형 데이터베이스 (0) | 2021.12.28 |
트랜잭션 (0) | 2021.11.21 |