복붙노트

[SPRING] @Transactional 읽기 전용 전파 스프링

SPRING

@Transactional 읽기 전용 전파 스프링

나는 하나의 트랜잭션 컨텍스트 내에서 Hibernate 엔티티와 함께 ​​작동하도록 웹 레이어를 허용하기 위해 커맨드 패턴을 사용하여 실험하고있다 (따라서 지연로드 예외를 피함). 나는 거래를 어떻게 처리해야하는지 혼란 스럽다.

내 명령은 @Transactional 주석으로 주석 처리 된 서비스 계층 메소드를 호출합니다. 이러한 서비스 계층 방법 중 일부는 읽기 전용입니다 (예 : @ Transactional (readOnly = true) - 일부는 읽기 / 쓰기입니다.

내 서비스 계층은 웹 계층을 대신하여 전달 된 명령을 실행하는 명령 처리기를 제공합니다.

@Transactional
public Command handle( Command cmd ) throws CommandException

나는 명령 핸들러의 handle () 메소드를 트랜잭션으로 만드는 것으로 생각한다. 명령의 구현이 여러 서비스 계층 메서드를 호출하는 경우 명령에서 호출 된 작업이 읽기 전용인지, 읽기 / 쓰기인지 또는 조합인지 여부를 명령 처리기가 알 수있는 방법이 없습니다 둘 중.

이 예제에서 전파가 작동하는 방식을 이해하지 못합니다. handle () 메서드를 readOnly = true로 설정하면 명령이 @Transactional (realOnly = false)으로 주석 된 서비스 계층 메서드를 호출하면 어떻게됩니까?

이 점에 대해 더 잘 이해해 주셔서 감사 드리며 귀하의 의견을 환영합니다 ...

앤드류

해결법

  1. ==============================

    1.우선 Spring은 지속성 자체를 수행하지 않으므로 readOnly가 정확히 무엇을 의미해야 하는지를 지정할 수 없습니다. 이 애트리뷰트는 제공자에게 힌트 일 뿐이고,이 경우 Hibernate에 의존한다.

    우선 Spring은 지속성 자체를 수행하지 않으므로 readOnly가 정확히 무엇을 의미해야 하는지를 지정할 수 없습니다. 이 애트리뷰트는 제공자에게 힌트 일 뿐이고,이 경우 Hibernate에 의존한다.

    readOnly를 true로 지정하면 현재 Hibernate Session에서 플러시 모드가 FlushMode.NEVER로 설정되어 세션이 트랜잭션을 커밋하지 못하도록합니다.

    또한 JDBC 커넥션에서 setReadOnly (true)가 호출됩니다. JDBC 커넥션은 기본 데이터베이스에 대한 힌트이기도합니다. 데이터베이스가 지원한다면 (대부분 가능), FlushMode.NEVER와 기본적으로 같은 효과가 있지만 수동으로 플러시 할 수 없기 때문에 더 강력합니다.

    이제 트랜잭션 전파가 어떻게 작동하는지 살펴 보겠습니다.

    명시 적으로 readOnly를 true로 설정하지 않으면 읽기 / 쓰기 트랜잭션을 갖게됩니다. 트랜잭션 속성 (예 : REQUIRES_NEW)에 따라 때때로 트랜잭션이 일시 중단되고 새 트랜잭션이 시작되어 결국 커밋되고 그 후 첫 번째 트랜잭션이 재개됩니다.

    좋아, 거의 다 왔어. 이 시나리오에서 readOnly를 가져 오는 것이 무엇인지 살펴 보겠습니다.

    읽기 / 쓰기 트랜잭션의 메소드가 readOnly 트랜잭션을 필요로하는 메소드를 호출하면 첫 번째 메소드가 일시 중단되어야합니다. 그렇지 않으면 두 번째 메소드가 끝날 때 플러시 / 커밋이 발생하기 때문입니다.

    반대로 읽기 / 쓰기가 필요한 readOnly 트랜잭션 내에서 메서드를 호출하면 플러시 / 커밋 할 수 없기 때문에 첫 번째 메서드가 일시 중단되고 두 번째 메서드는이를 필요로합니다.

    readOnly-to-readOnly 및 읽기 / 쓰기 - 읽기 / 쓰기의 경우 외부 트랜잭션은 일시 중지 할 필요가 없습니다 (그렇지 않으면 명백하게 전파를 지정하지 않는 한).

  2. ==============================

    2.이전 트랜잭션 계속 이후 readOnly = true에서 readOnly = false를 호출하면 작동하지 않습니다.

    이전 트랜잭션 계속 이후 readOnly = true에서 readOnly = false를 호출하면 작동하지 않습니다.

    귀하의 예에서, 서비스 계층의 handle () 메소드는 새로운 읽기 - 쓰기 트랜잭션을 시작합니다. handle 메서드가 차례로 읽기 전용 주석이 붙은 서비스 메서드를 호출하면 읽기 전용은 기존의 읽기 쓰기 트랜잭션에 참여하므로 아무런 영향을주지 않습니다.

    이러한 메소드가 읽기 전용이어야 할 경우 Propagation.REQUIRES_NEW를 사용하여 주석을 달 수 있으며 기존의 읽기 - 쓰기 트랜잭션에 참여하는 대신 새로운 읽기 전용 트랜잭션을 시작합니다.

    다음은 실습 예제입니다. CircuitStateRepository는 스프링 데이터 JPA 저장소입니다.

    BeanS는 트랜잭션을 = 읽기 전용 Bean1이라고 부르며 조회를 수행하고 새 객체를 저장하는 transactional = read-write Bean2를 호출합니다.

    31 09 : 39 : 44.199 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - 이름이 [nz.co.vodafone.wcim.business.Bean1.startSomething] 인 새 트랜잭션 만들기 : PROPAGATION_REQUIRED, ISOLATION_DEFAULT, readOnly; ''

    31 09 : 39 : 44.230 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - 기존 트랜잭션에 참여

    데이터베이스에 커밋 된 것이 없습니다.

    이제 Bean2 @Transactional 주석을 변경하여 propagation = Propagation을 추가하십시오 .REQUIRES_NEW

    31 09 : 31 : 36.418 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - 이름이 [nz.co.vodafone.wcim.business.Bean1.startSomething] 인 새 트랜잭션 만들기 : PROPAGATION_REQUIRED, ISOLATION_DEFAULT, readOnly; ''

    31 09 : 31 : 36.449 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - 현재 트랜잭션을 일시 중단하고 이름이 [nz.co.vodafone.wcim.business.Bean2.createSomething] 인 새 트랜잭션을 생성합니다.

    그리고 Bean2에 의한 변경은 이제 데이터베이스에 커밋됩니다.

    다음은 스프링 데이터, 최대 절전 모드 및 오라클로 테스트 한 예제입니다.

    @Named
    public class BeanS {
    
    @Inject
    Bean1 bean1;
    
    @Scheduled(fixedRate = 20000)
    public void runSomething() {
        bean1.startSomething();
    }
    
    }
    
    
    @Named
    @Transactional(readOnly = true)
    public class Bean1 {
    
    Logger log = LoggerFactory.getLogger(Bean1.class);
    
    @Inject
    private CircuitStateRepository csr;
    
    @Inject
    private Bean2 bean2;
    
    public void startSomething() {
    
        Iterable<CircuitState> s = csr.findAll();
        CircuitState c = s.iterator().next();
        log.info("GOT CIRCUIT {}", c.getCircuitId());
        bean2.createSomething(c.getCircuitId());
    
    }
    
    }
    
    
    @Named
    @Transactional(readOnly = false)
    public class Bean2 {
    
        @Inject
        CircuitStateRepository csr;
    
        public void createSomething(String circuitId) {
    
            CircuitState c = new CircuitState(circuitId + "-New-" + new DateTime().toString("hhmmss"), new DateTime());
    
            csr.save(c);
    
        }
    
    
    }
    
  3. ==============================

    3.기본적으로 트랜잭션 전파는 필수적입니다. 즉, 동일한 트랜잭션이 트랜잭션 호출자에서 트랜잭션 호출 수신자로 전파됩니다. 이 경우에도 읽기 전용 상태가 전파됩니다. 예 : 읽기 전용 트랜잭션이 읽기 - 쓰기 트랜잭션을 호출하면 전체 트랜잭션이 읽기 전용이됩니다.

    기본적으로 트랜잭션 전파는 필수적입니다. 즉, 동일한 트랜잭션이 트랜잭션 호출자에서 트랜잭션 호출 수신자로 전파됩니다. 이 경우에도 읽기 전용 상태가 전파됩니다. 예 : 읽기 전용 트랜잭션이 읽기 - 쓰기 트랜잭션을 호출하면 전체 트랜잭션이 읽기 전용이됩니다.

    게으른로드를 허용하기 위해보기 패턴에서 열린 세션을 사용할 수 있습니까? 그렇게하면 핸들 메서드가 트랜잭션 일 필요가 전혀 없습니다.

  4. ==============================

    4.현재 활성 트랜잭션에 대한 설정을 무시하는 것처럼 보이지만 새 트랜잭션에만 설정을 적용합니다.

    현재 활성 트랜잭션에 대한 설정을 무시하는 것처럼 보이지만 새 트랜잭션에만 설정을 적용합니다.

  5. from https://stackoverflow.com/questions/1614139/spring-transactional-read-only-propagation by cc-by-sa and MIT license