트랜잭션 적용 확인 및 우선순위

바그다드·2023년 8월 2일
0

스프링 트랜잭션

목록 보기
1/5

우리는 스프링에서 제공하는 @Transcational을 이용해 여러 작업을 하나의 트랜잭션으로 쉽게 묶어 처리할 수 있다. 또한 스프링에서는 트랜잭션 추상화를 제공할 뿐만 아니라 프로젝트에 사용되는 데이터 접근 기술을 인식해서 적절한 구현체를 빈으로 등록해준다.

트랜잭션 AOP

@Transcational을 사용하기 전 JDBC를 예로 들어보자. 하나의 쿼리문을 실행하기 위해서 커넥션을 획득하고, Preparedstatement를 생성하는 등 반복되는 코드가 DB에 접근하는 모든 메서드에 동일하게 작성되었다. @Transcational은 이런 문제를 한번에 해결해준다. @Transcational은 프록시를 적용해 트랜잭션을 처리하는 객체와 비즈니스 로직을 처리하는 서비스 객체를 명확하게 분리할 수 있게 해준다.

흐름은 다음과 같다.

  1. 외부에서 서비스의 트랜잭션 로직을 호출한다.
  2. 프록시 객체가 호출되고 트랜잭션 매니저를 획득한다.
    • 실제 서비스가 아니라 서비스를 상속받은 프록시 객체가 호출된다.
  3. 트랜잭션 매니져로부터 트랜잭션을 획득한다.
  4. DataSource를 이용해 커넥션을 생성한다.
  5. con.setAutoCommit(false)로 커넥션을 시작한다.
  6. 트랜잭션 동기화 매니져에 커넥션을 보관한다.
  7. 커넥션이 보관된다.
  8. 프록시 객체가 실제 서비스 로직을 호출한다.
  9. 데이터 접근 로직은 트랜잭션 동기화 매니져를 통해 커넥션을 동기화 한다.

트랜잭션 적용 확인

그럼 트랜잭션이 적용되었는지 어떻게 확인할 수 있을까?
코드로 알아보자.

@Slf4j
@SpringBootTest
public class TxBasicTest {
	// 빈 등록 등 생략
    @Test
    void proxyCheck() {
        log.info("aop class={}", basicService.getClass());
        assertThat(AopUtils.isAopProxy(basicService)).isTrue();
    }

    @Test
    void txTest() {
        basicService.tx();
        basicService.nonTx();
    }
	
    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);
        }
    }

}
  • 트랜잭션 시작과 종료를 명확하게 로그로 남길 수 있게 아래 코드를 추가하자
logging.level.org.springframework.transaction.interceptor=TRACE

  • 프록시 객체가 주입된 것과 트랜잭션 적용 여부를 확인할 수 있다.

트랜잭션 우선순위

스프링은 언제나 그렇듯이 자세하고 구체적인 것이 더 높은 우선순위를 가진다. 이는 트랜잭션 적용에 있어서도 마찬가지다. 코드로 바로 확인하자.

    @Slf4j
    @Transactional(readOnly = true)
    static class LevelService {

        // Transactional의 경우 default가 readOnly=false이기 때문에
        // 파라미터값은 지워도 됨
        @Transactional(readOnly = false)
        public void write() {
            log.info("call write");
            printTxInfo();
        }

        public void read() {
            log.info("call read");
            printTxInfo();
        }
  • @Transactional이 클래스 단위와 메서드 단위에 붙어있는데,
    클래스 단위에는 readOnly = true라는 속성으로 읽기 전용으로 사용되고
    메서드 단위에는 readOnly = false로 읽기 쓰기 모두 가능하게 적용이 되었다.

결과를 확인해보자.

출처 : 김영한 - 스프링 DB 2편

profile
꾸준히 하자!

0개의 댓글