일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- aws cli s3 exclude directory
- AWS IAM MFA
- IAM MFA
- 멀티모듈
- oracle cloud
- json type column 생성
- mysql json type
- h2 intellij
- jpa 환경에서 json type 활용하기
- in memory
- 선언적 트랜잭션
- 무료 인스턴스
- jenkins 배포
- json type index
- Transactiona
- Jenkins
- spring boot 멀티모듈
- 프리티어 인스턴스
- aws cli s3 cp
- h2 연동
- Oracle Linux 8
- jpa json type
- 트랜잭션 전파속성
- IAM이란
- aws cli s3
- server mode
- multi module
- aws cli s3 exclude folder
- json type 활용
- JSON type
- Today
- Total
Chris Devlog
선언적 트랜잭션(@Transactional)의 트랜잭션 전파속성 본문
선언적 트랜잭션
AOP를 이용해 코드 외부에서 트랜잭션의 기능을 부여해 주고 속성을 지정할 수 있게 하는 방법
@Slf4j
@Service
public class UserServiceImpl implements UserService {
@Transactional
public void add(){
try{
...
} catch(Exception e) {
log.error(e.getMessage(), e);
}
}
}
프로그램에 의한 트랜잭션
TransactionTemplate이나 개별 데이터 기술의 트랜잭션 API를 사용해 직접 코드안에서 사용하는 방법
@Slf4j
@Service
public class UserServiceImpl implements UserService {
private PlatformTransactionManager transactionManager;
@Autowired
UserServiceImpl(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public add(){
TransactionStatus jobStatus = transactionManager
.getTransaction(new DefaultTransactionDefinition());
try{
...
} catch(Exception e) {
jobStatus.setRollbackOnly();
log.error(e.getMessage(), e);
} finally {
if (jobStatus.isRollbackOnly()) {
transactionManager.rollback(jobStatus);
} else {
transactionManager.commit(jobStatus);
}
}
}
}
트랜잭션 전파
트랜잭션의 경계에서 이미 진행중인 트랜잭션이 있을때 또는 없을때 어떻게 동작할 것인가를 결정하는 방식
트랜잭션 전파속성

위 그림처럼 각각 독립적인 트랜잭션 경계를 가진 두개의 코드에서 A의 트랜잭션이 시작되고 B를 호출 했을 때, 이미 진행중인 트랜잭션(A의 트랜잭션)이 어떻게 영향을 미칠 수 있는가를 정의 하는것
- REQUIRED
- 기본 설정값
- 이미 시작된 트랜잭션이 있으면 참여
- 시작된 트랜잭션이 없으면 새 트랜잭션 생성
- SUPPORTS
- 이미 시작된 트랜잭션이 있으면 참여
- 시작된 트랜잭션이 없으면 비 트랜잭션으로 실행
- MANDATORY
- 이미 시작된 트랜잭션이 있으면 참여
- 시작된 트랜잭션이 없는 경우 예외를 발생
- REQUIRES_NEW
- 이미 시작된 트랜잭션이 있어도 새 트랜잭션 생성
- 시작된 트랜잭션이 있으면 일시 중단
- NOT_SUPPORTED
- 이미 시작된 트랜잭션이 있으면 일시 중단
- 비 트랜잭션으로 실행
- NEVER
- 비 트랜잭션으로 실행
- 트랜잭션이 있으면 예외를 발생
- NESTED
중첩(자식) 트랜잭션을 커밋/중단해도 상위 트랜잭션의 상태에는 영향을 미치지 않음상위(부모) 트랜잭션이 중단되면 중첩(자식) 트랜잭션도 중단상위(부모) 트랜잭션은 하위 트랜잭션이 수정한 내용확인 가능중첩 트랜잭션은 부모가 커밋할 때까지 부모 트랜잭션에 의해 유지되며, 부모가 커밋하면 자식 트랜잭션이 수행한 모든 수정 사항도 커밋- 아쉽게도 JPA(Hibernate 구현체)에서 중첩 트랜잭션 사용시 오류 발생
- 현업에서 JPA를 사용중이므로 오류 테스트만 진행
Test
REQUIRED
- 이미 시작된 트랜잭션이 있으면 참여
@Slf4j
@Service
@RequiredArgsConstructor
public class AService {
private final BService bService;
@Transactional
public void requiredAtoB() {
log.info("AService Transaction active is {}", TransactionSynchronizationManager.isActualTransactionActive());
bService.noneTransactional();
}
}
@Slf4j
@Service
public class BService {
public void noneTransactional() {
log.info("BService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
}
}
// Console
Getting transaction for [com.chirs.mybook.domain.transaction_propagation.service.AService.requiredAtoB]
AService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.AService.requiredAtoB] active is [true]
BService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.AService.requiredAtoB] active is [true]
Completing transaction for [com.chirs.mybook.domain.transaction_propagation.service.AService.requiredAtoB]
- 시작된 트랜잭션이 없으면 새 트랜잭션 생성
@Slf4j
@Service
@RequiredArgsConstructor
public class AService {
private final BService bService;
public void requiredB() {
log.info("AService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
bService.required();
}
}
@Slf4j
@Service
public class BService {
@Transactional(propagation = Propagation.REQUIRED)
public void required() {
log.info("BService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
}
}
// Console
AService Transaction name [null] active is [false]
Getting transaction for [com.chirs.mybook.domain.transaction_propagation.service.BService.required]
BService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.BService.required] active is [true]
Completing transaction for [com.chirs.mybook.domain.transaction_propagation.service.BService.required]
SUPPORTS
- 이미 시작된 트랜잭션이 있으면 참여
@Slf4j
@Service
@RequiredArgsConstructor
public class AService {
private final BService bService;
@Transactional
public void supportsAtoB() {
log.info("AService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
bService.supports();
}
}
@Slf4j
@Service
public class BService {
@Transactional(propagation = Propagation.SUPPORTS)
public void supports() {
log.info("BService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
}
}
// Console
Getting transaction for [com.chirs.mybook.domain.transaction_propagation.service.AService.supportsAtoB]
AService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.AService.supportsAtoB] active is [true]
Getting transaction for [com.chirs.mybook.domain.transaction_propagation.service.BService.supports]
BService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.AService.supportsAtoB] active is [true]
Completing transaction for [com.chirs.mybook.domain.transaction_propagation.service.BService.supports]
Completing transaction for [com.chirs.mybook.domain.transaction_propagation.service.AService.supportsAtoB]
- 시작된 트랜잭션이 없는경우 비 트랜잭션으로 실행
@Slf4j
@Service
@RequiredArgsConstructor
public class AService {
private final BService bService;
public void supportsB() {
log.info("AService Transaction active is {}",
TransactionSynchronizationManager.isActualTransactionActive());
bService.supports();
}
}
@Slf4j
@Service
public class BService {
@Transactional(propagation = Propagation.SUPPORTS)
public void supports() {
log.info("BService Transaction active is {}",
TransactionSynchronizationManager.isActualTransactionActive());
}
}
// Console
AService Transaction name [null] active is [false]
Getting transaction for [com.chirs.mybook.domain.transaction_propagation.service.BService.supports]
BService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.BService.supports] active is [false]
Completing transaction for [com.chirs.mybook.domain.transaction_propagation.service.BService.supports]
Mandatory
- 이미 시작된 트랜잭션이 있으면 참여
@Slf4j
@Service
@RequiredArgsConstructor
public class AService {
private final BService bService;
@Transactional
public void mandatoryAtoB() {
log.info("AService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
bService.mandatory();
}
}
@Slf4j
@Service
public class BService {
@Transactional(propagation = Propagation.MANDATORY)
public void mandatory() {
log.info("BService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
}
}
// Console
Getting transaction for [com.chirs.mybook.domain.transaction_propagation.service.AService.mandatoryAtoB]
AService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.AService.mandatoryAtoB] active is [true]
Getting transaction for [com.chirs.mybook.domain.transaction_propagation.service.BService.mandatory]
BService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.AService.mandatoryAtoB] active is [true]
Completing transaction for [com.chirs.mybook.domain.transaction_propagation.service.BService.mandatory]
Completing transaction for [com.chirs.mybook.domain.transaction_propagation.service.AService.mandatoryAtoB]
- 시작된 트랜잭션이 없는 경우 예외를 발생
@Slf4j
@Service
@RequiredArgsConstructor
public class AService {
private final BService bService;
public void mandatoryB() {
log.info("AService Transaction active is {}",
TransactionSynchronizationManager.isActualTransactionActive());
bService.mandatory();
}
}
@Slf4j
@Service
public class BService {
public void mandatoryB() {
log.info("AService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
bService.mandatory();
}
}
// Console
AService Transaction name [null] active is [false]
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'] with root cause
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
REQUIRES_NEW
- 이미 시작된 트랜잭션이 있어도 새 트랜잭션 생성하며, 시작된 트랜잭션이 있으면 일시 중단
@Slf4j
@Service
@RequiredArgsConstructor
public class AService {
private final BService bService;
@Transactional
public void requiresNew() {
log.info("AService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
bService.requiresNew();
}
}
@Slf4j
@Service
public class BService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiresNew() {
log.info("BService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
}
}
// Console
Getting transaction for [com.chirs.mybook.domain.transaction_propagation.service.AService.requiresNew]
AService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.AService.requiresNew] active is [true]
Getting transaction for [com.chirs.mybook.domain.transaction_propagation.service.BService.requiresNew]
BService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.BService.requiresNew] active is [true]
Completing transaction for [com.chirs.mybook.domain.transaction_propagation.service.BService.requiresNew]
Completing transaction for [com.chirs.mybook.domain.transaction_propagation.service.AService.requiresNew]
NOT_SUPPORTED
- 이미 시작된 트랜잭션이 있으면 일시 중단 -> 비 트랜잭션으로 실행
@Slf4j
@Service
@RequiredArgsConstructor
public class AService {
private final BService bService;
@Transactional
public void notSupported() {
log.info("AService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
bService.notSupported();
}
}
@Slf4j
@Service
public class BService {
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void notSupported() {
log.info("BService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
}
}
// Console
Getting transaction for [com.chirs.mybook.domain.transaction_propagation.service.AService.notSupported]
AService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.AService.notSupported] active is [true]
Getting transaction for [com.chirs.mybook.domain.transaction_propagation.service.BService.notSupported]
BService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.BService.notSupported] active is [false]
Completing transaction for [com.chirs.mybook.domain.transaction_propagation.service.BService.notSupported]
Completing transaction for [com.chirs.mybook.domain.transaction_propagation.service.AService.notSupported]
NEVER
- 비 트랜잭션으로 실행 -> 트랜잭션이 있으면 예외를 발생
@Slf4j
@Service
@RequiredArgsConstructor
public class AService {
private final BService bService;
@Transactional
public void never() {
log.info("AService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
bService.never();
}
}
@Slf4j
@Service
public class BService {
@Transactional(propagation = Propagation.NEVER)
public void never() {
log.info("BService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
}
}
// Console
Getting transaction for [com.chirs.mybook.domain.transaction_propagation.service.AService.never]
AService Transaction name [com.chirs.mybook.domain.transaction_propagation.service.AService.never] active is [true]
Completing transaction for [com.chirs.mybook.domain.transaction_propagation.service.AService.never] after exception: org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'] with root cause
NESTED
- 현업에서 JPA를 사용중이므로 오류 테스트만 진행
@Slf4j
@Service
@RequiredArgsConstructor
public class AService {
private final BService bService;
@Transactional
public void nested() {
log.info("AService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
bService.nested();
}
}
@Slf4j
@Service
public class BService {
@Transactional(propagation = Propagation.NESTED)
public void nested() {
log.info("BService Transaction name [{}] active is [{}]",
TransactionSynchronizationManager.getCurrentTransactionName(),
TransactionSynchronizationManager.isActualTransactionActive());
}
}
// Console
NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities
참조
https://docs.oracle.com/cd/E17276_01/html/gsg_xml_txn/java/nestedtxn.html
Nested Transactions
A nested transaction is used to provide a transactional guarantee for a subset of operations performed within the scope of a larger transaction. Doing this allows you to commit and abort the subset of operations independently of the larger transaction. The
docs.oracle.com
https://reiphiel.tistory.com/entry/understanding-of-spring-transaction-management-practice
Spring 트랜잭션 관리의 이해 - 실전편
Spring 트랜잭션 관리방법 Spring(스프링)에서 트랜잭션(Transaction)을 관리하는 방법은 크게 서로 대비되는 2가지 방법으로 나눌 수 있습니다. 프로그램에 의한(Programmatic) 트랜잭션 관리 첫번째로 알
reiphiel.tistory.com
토비의 스프링 3.1 - 6.6 트랜잭션 속성 ~
'Spring Boot' 카테고리의 다른 글
[Spring Boot] Multi Module Project feat. jdk17, Gradle 7.5.1 (0) | 2022.11.30 |
---|---|
[H2] In-Memory feat.Spring boot, JPA, IntelliJ, tcp (0) | 2022.07.13 |