June 11, 2023
1. 클라이언트
-> 2. 서비스(트랜잭션 처리 로직)
-> 3. 리포지토리(데이터 접근 로직)
2의 서비스에는 그냥 비즈니스 로직만 있는 것이 아니라 트랜잭션 시작, 끝의 로직에 다 들어있다.
트랜잭션 시작
비즈니스 로직
트랜잭션 종료
- 트랜잭션 사용 코드 예시
```java
// 트랜잭션 시작
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
//비즈니스 로직
bizLogic(fromId, toId, money);
transactionManager.commit(status); //성공시 커밋
} catch (Exception e) {
transactionManager.rollback(status); //실패시 롤백
throw new IllegalStateException(e);
}
1. 클라이언트
—(프록시 호출)—> 2. 트랜잭션 프록시(트랜잭션 처리 로직)
----> 3. 서비스(비즈니스 로직)
-----> 4. 리포지토리(데이터 접근 로직)
2단계의 트랜잭션 프록시 코드 예시
public class TransactionProxy {
private MemberService target;
public void logic() { //트랜잭션 시작
TransactionStatus status = transactionManager.getTransaction(..);
try {
//실제 대상 호출
target.logic();
transactionManager.commit(status); //성공시 커밋
} catch (Exception e) {
transactionManager.rollback(status); //실패시 롤백
throw new IllegalStateException(e);
}
}
}
트랜잭션 적용 후 3단계의 서비스 코드 예시
public class Service {
public void logic() {
// 트랜잭션 관련 코드 제거, 순수 비즈니스 로직만 남음
bizLogic(fromId, toId, money);
}
}
## 트랜잭션 적용(프록시) 확인하기
- `AopUtils.isAopProxy()` 로 프록시 객체 확인가능
- [스프링 공식 문서](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/aop/support/AopUtils.html#isAopProxy(java.lang.Object))
- `TransactionSynchronizationManager.isActualTransactionActive()` 로 트랜잭션 확인
- [스프링 공식 문서](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/support/TransactionSynchronizationManager.html#isActualTransactionActive())
- 코드로 확인하기
- spring boot 2.6,x, java11, gradle
- lombok, spring data jpa, h2 database
```java
@Slf4j
@SpringBootTest
public class TxBasicTest {
@Autowired
BasicService basicService;
@Test
void proxyCheck() {
//BasicService$$EnhancerBySpringCGLIB...
log.info("aop class={}", basicService.getClass());
assertThat(AopUtils.isAopProxy(basicService)).isTrue();
}
@Test
void txTest() {
basicService.tx();
basicService.nonTx();
}
@TestConfiguration
static class TxApplyBasicConfig {
@Bean
BasicService basicService() {
return new BasicService();
}
}
@Slf4j
static class BasicService {
@Transactional
public void tx() {
log.info("call tx");
boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
log.info("tx active={}, txActive";
}
public void nonTx() {
log.info("call nonTx");
boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
log.info("tx active={}, txActive";
}
}
}
proxyCheck() - 실행 결과
TxBasicTest : aop class=class ..$BasicService$$EnhancerBySpringCGLIB$$xxxxxx
스프링 컨테이너에 트랜잭션 프록시 등록 (매우 중요) - 위의 구조 설명
txTest() 실행
로그 추가
application.properties
logging.level.org.springframework.transaction.interceptor=TRACE
basicService.tx() 호출
TransactionSynchronizationManager.isActualTransactionActive()
실행 결과
#tx() 호출
TransactionInterceptor : Getting transaction for [..BasicService.tx]
y.TxBasicTest$BasicService : call tx
y.TxBasicTest$BasicService : tx active=true
TransactionInterceptor : Completing transaction for [..BasicService.tx]
y.TxBasicTestBasicService : tx active=false
- 로그를 통해 tx() 호출시에는 tx active=true 를 통해 트랜잭션이 적용된 것을 확인할 수 있다.
- TransactionInterceptor 로그를 통해 트랜잭션 프록시가 트랜잭션을 시작하고 완료한 내용을 확인할
수 있다.
- nonTx() 호출시에는 tx active=false 를 통해 트랜잭션이 없는 것을 확인할 수 있다.
> 출처 : 인프런의 김영한 강의, 스프링 DB 2편 - 데이터 접근 활용 기술(스프링 트랜잭션 이해)