Programming-[Backend]/Spring

[TIL] 스프링 트랜잭션 따로 적용 하기 (REQUIRES_NEW), 클래스 분리 필요

컴퓨터 탐험가 찰리 2022. 5. 21. 20:16
728x90
반응형

 

스프링에서 트랜잭션을 분리하고 싶을 때, @Transactional(propagation = Propagation.REQUIRES_NEW)로 트랜잭션을 분리하고자 하는 메서드에 어노테이션을 달아주면된다. 이외에도 @Transactional을 분리하는 종류에 대해서는 이전에 정리했었다.

[TIL][링크] 트랜잭션의 전파, Spring @Transactional 중첩

 

 

그러나 이때, 트랜잭션을 분리하고자 하는 메서드를 반드시 클래스를 분리해서 적용해야한다. 이것은 @Transactional 어노테이션이 Spring의 CGLIB Proxy를 기반으로 동작하기 때문이다. 다시 말해 동일한 Bean으로 등록된 클래스의 메서드에서는 @Transactional을 단일 건으로 취급한다. Proxy로 불러온 빈은 다른 클래스가 아닌 경우 인터셉트되어 전달되지 않기 때문에 @Transactional이 동작하지 않는다.

 

트랜잭션이 분리될 것으로 예상하지만, 실제로는 분리되지 않는 코드

public class saveService(XxxEntity xxxEntity) {

  //...

  @Transactional
  public void totalSave(XxxEntity xxxEntity) {
    //save 로직
    //savePart1(xxxEntity);
  }

  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void savePart1(XxxEntity xxxEntity) {
    //savePart1 로직 - xxxEntity 이용
  }
  
  
}

 

 

 

 

(경험상 기록 내용)

또한 프록시 객체로 불러온 Entity 등은 클래스를 분리하더라도 정상적인 동작이 되지 않을 수 있다. 따라서 id 값 등을 이용하여 Repository를 통해 DB에서 다시 객체를 조회하였다. 아마 이것도 Entity를 프록시 객체 형태로 가져오면 캐시에 저장된 값들을 가져올뿐, DB에 접근하여 SQL문을 commit은 하지 않기 때문인 것 같다.

 

트랜잭션 정상 분리 코드 : 클래스를 분리

public class saveService(XxxEntity xxxEntity) {

  //...

  @Transactional
  public void totalSave(XxxEntity xxxEntity) {
    //save 로직
    
    //변경 : xxxEntity의 id값 전달
    //savePart1(xxxEntity.getId());
  }
}


public class savePart1Service(Long xxxId) {

  //...

  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void savePart1(Long id) {
    
    //추가 : DB 접근하여 entity 재조회
    XxxEntity xxxEntity = xxxRepository.findById(id).orElseThrow(.....);
    
    //savePart1 로직 실행
  }
}

 

 

 


 

참조

 

1. popit - 동일한 Bean(Class)에서 @Transactional 동작 방식

https://www.popit.kr/%EB%8F%99%EC%9D%BC%ED%95%9C-beanclass%EC%97%90%EC%84%9C-transactional-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D/

728x90
반응형