스프링DB

스프링 트랜잭션 내부 호출시 발생하는 문제

lby132 2023. 6. 1. 00:20

스프링 트랜잭션을 사용하면 즉 @Transactional을 사용하면 AOP가 적용되어서 프록시 방식으로 AOP를 사용한다.

프록시 객체가 요청을 먼저 받아서 트랜잭션을 처리하고, 실제 객체를 호출해준다. 그렇기 때문에 트랜잭션을 적용하려면 항상 프록시를 통해 비즈니스 로직을 수행해야한다. (프록시를 통해서 대상 객체(Target) 호출) 이렇게 해야 프록시에 먼저 트랜잭션을 적용하고 이후에 대상 객체를 호출하게 되는데 만약 프록시를 거치지 않고 대상 객체를 직접 호출 하게 되면 AOP가 적용되지 않고, 트랜잭션도 적용되지 않게 된다. 코드로 상황을 보면 이렇다.

@Slf4j
static class CallService {
    public void external() {
        log.info("call external");
        printTxInfo();
        internal(); // 프록시를 거치지 않고 대상 객체 직접 호출
    }

    @Transactional
    public void internal() {
        log.info("call internal");
        printTxInfo();
    }

    private void printTxInfo() {
        boolean txActive = TransactionSynchronizationManager.isActualTransactionActive(); //현재 스레드가 트랜잭션이 적용이 되어 있는지 확 
        log.info("tx active={}", txActive);
        boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); //readOnly인지 판별
        log.info("tx readOnly={}", readOnly);
    }
}

이렇게 되면

콘솔창인데 tx active를 보면 false로 되어있다. 트랜잭션이 적용되지 않았다.

 

해결 방안으로는 external()과 internal()을 분리해서 외부에서 호출하는 방식으로 해서 프록시를 통해 접근하도록 하면서 트랜잭션을 적용시켜야 한다.

 

@Slf4j
@RequiredArgsConstructor
static class CallService {

    private final InternalService internalService;

    public void external() {
        log.info("call external");
        printTxInfo();
        internalService.internal();
    }

    private void printTxInfo() {
        boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
        log.info("tx active={}", txActive);
        boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
        log.info("tx readOnly={}", readOnly);
    }
}

static class InternalService {

    @Transactional
    public void internal() {
        log.info("call internal");
        printTxInfo();
    }

    private void printTxInfo() {
        boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
        log.info("tx active={}", txActive);
        boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
        log.info("tx readOnly={}", readOnly);
    }
}

 

이렇게 하면 트랜잭션이 잘 적용된걸 볼 수 있다.

'스프링DB' 카테고리의 다른 글

transaction log  (0) 2023.06.01
초기화 시점에 트랜잭션적용  (0) 2023.06.01
트랜잭션 전파  (0) 2022.09.16
트랜잭션 이해  (0) 2022.09.15
트랜잭션 AOP 주의 사항  (0) 2022.09.14