본문 바로가기
Spring/Spring-detail

@Transactional 안에 @Transactional 테스트

by YoonJong 2024. 3. 4.
728x90

@Transactional 안에 @Transactional 이 있는 경우 테스트를 해보았습니다.

TransactionSynchronizationManager.*isActualTransactionActive*() 메서드를 이용해서 트랜잭션이 적용되는지 확인 할 수 있습니다.



1. ### 외부메서드(트랜잭션) + 이너메서드

@Slf4j
@Service
public class TransactionService {

    @Transactional
    public void outerMethod() {
        log.info(" ****** outerMethod start ******");
        log.info(" ****** outerMethod transaction Active : {}" ,
            TransactionSynchronizationManager.isActualTransactionActive());
        innerMethod();
        log.info(" ****** outerMethod end ******");
    }

    public void innerMethod() {
        log.info(" ****** innerMethod start ******");
        log.info(" ****** innerMethod transaction Active : {}" ,
            TransactionSynchronizationManager.isActualTransactionActive());
        log.info(" ****** innerMethod end ******");
    }
}


@Test
@DisplayName("외부 메서드(트랜잭션) + 내부 메서드")
void test() {
    transactionService.outerMethod();
}


//테스트 결과 
****** outerMethod start ******
****** outerMethod transaction Active : true
****** innerMethod start ******
****** innerMethod transaction Active : true
****** innerMethod end ******
****** outerMethod end ******



2. ### 외부메서드 + 이너메서드(트랜잭션)
해당 테스트를 진행할 경우, 인텔리제이에서 아래와 같은 경고 표시를 나타내줍니다.

@Slf4j
@Service
public class TransactionService {


    public void outerMethod() {
        log.info(" ****** outerMethod start ******");
        log.info(" ****** outerMethod transaction Active : {}" ,
            TransactionSynchronizationManager.isActualTransactionActive());
        innerMethod();
        log.info(" ****** outerMethod end ******");
    }

    @Transactional
    public void innerMethod() {
        log.info(" ****** innerMethod start ******");
        log.info(" ****** innerMethod transaction Active : {}" ,
            TransactionSynchronizationManager.isActualTransactionActive());
        log.info(" ****** innerMethod end ******");
    }
}


@Test
@DisplayName("외부 메서드 + 내부 메서드(트랜잭션)")
void test() {
    transactionService.outerMethod();
}

// 테스트 결과
****** outerMethod start ******
****** outerMethod transaction Active : false
****** innerMethod start ******
****** innerMethod transaction Active : false
****** innerMethod end ******
****** outerMethod end ******

 

3. ### 외부메서드 + 다른클래스의 이너메서드(트랜잭션)
다른 클래스의 메서드에 @Transactional 을 적용시키고 사용하면 해당 메서드는 트랜잭션이 적용된다.

@Slf4j
@Service
public class OtherService {

    @Transactional
    public void otherInnerMethod() {
        log.info(" ****** outer innerMethod start ******");
        log.info(" ****** outer innerMethod transaction Active : {}" ,
            TransactionSynchronizationManager.isActualTransactionActive());
        log.info(" ****** outer innerMethod end ******");
    }
}

@Slf4j
@Service
@RequiredArgsConstructor
public class TransactionService {

    private final OtherService otherService;

    public void outerMethod() {
        log.info(" ****** outerMethod start ******");
        log.info(" ****** outerMethod transaction Active : {}" ,
            TransactionSynchronizationManager.isActualTransactionActive());
        otherService.otherInnerMethod();
        log.info(" ****** outerMethod end ******");
    }


@Test
@DisplayName("외부 메서드 + 다른 클래스 내부 메서드(트랜잭션)")
void test() {
    transactionService.outerMethod();
}


// 테스트 결과
****** outerMethod start ******
****** outerMethod transaction Active : false
****** outer innerMethod start ******
****** outer innerMethod transaction Active : true
****** outer innerMethod end ******
****** outerMethod end ******


4. ### 외부 메서드(트랜잭션) + 다른 클래스 내부 메서드

@Slf4j
@Service
public class OtherService {

    public void otherInnerMethod() {
        log.info(" ****** outer innerMethod start ******");
        log.info(" ****** outer innerMethod transaction Active : {}" ,
            TransactionSynchronizationManager.isActualTransactionActive());
        log.info(" ****** outer innerMethod end ******");
    }
}

@Slf4j
@Service
@RequiredArgsConstructor
public class TransactionService {

    private final OtherService otherService;

    @Transactional
    public void outerMethod() {
        log.info(" ****** outerMethod start ******");
        log.info(" ****** outerMethod transaction Active : {}" ,
            TransactionSynchronizationManager.isActualTransactionActive());
        otherService.otherInnerMethod();
        log.info(" ****** outerMethod end ******");
    }
}


@Test
@DisplayName("외부 메서드(트랜잭션) + 다른 클래스 내부 메서드")
void test() {
    transactionService.outerMethod();
}

// 결과
****** outerMethod start ******
****** outerMethod transaction Active : true
****** outer innerMethod start ******
****** outer innerMethod transaction Active : true
****** outer innerMethod end ******
****** outerMethod end ******



## 정리
- @Transactional 은 AOP 를 사용하여 구현된다.
    - 트랜잭션은 객체 외부에서 처음 진입하는 메서드를 기준으로 동작한다. ( AOP Proxy는 호출시점에 target을 가로챈다 )
    - private 메서드에서는 작동되지 않는다.
    - 기본적으로 CGLib 을 사용한다 ( 인터페이스가 있으면 동적 Proxy 사용 )
- 외부에서 @Transactional 이 적용되지 않다면 내부 이너메서드에 적용이 되어있더라도 적용되지 않는다.
- 이너메서드에 적용시키고 싶으면, 다른 클래스를 만들어 사용해야 한다.
- @Transactional 의 전파속성의 기본속성은 REQURED 이다 ( 진행중인 트랜잭션 내부에 새로운 트랜잭션이 들어오면 기존 트랜잭션이 참여 )

인프런 영한쌤 답변 캡쳐

728x90

댓글