6

독립 실행 형 Java 응용 프로그램의 일부로 실행되는 데이터 액세스 클래스가 있습니다. 현재 트랜잭션 관리자가 정의되어 있지만 트랜잭션의 범위를 줄이기 위해 클래스를 리팩터링하고 싶습니다. 그러나 얻을 수 있다면 org.hibernate.HibernateException : 스레드에 바인드 된 최대 절전 모드 세션이 없으며 구성이 없습니다. 여기서 트랜잭션이 아닌 트랜잭션을 생성 할 수 있습니다. 이것은 @ Transaction을 이동하면 어떻게 든 트랜잭션이 인식되는 것을 중단했음을 의미합니다.비 트랜잭션 부분을 분리하는 @Transactional 메소드를 리팩토링하는 방법

내 원래 버전은 리팩토링 된 메소드가 비공개이지만 해당 주석을 선택하지 않은 경우와 같이 공개로 변경할 것을 권장합니다. 내가 원하는 무엇

public class DoStuff { 
    @Transactional 
    public void originalMethod() { 
     // do database stuff 
     ... 

     // do non-database stuff that is time consuming 
     ... 
    } 
} 

는 다음

public class DoStuff { 
    public void originalMethod() { 
     doDatabaseStuff() 

     doNonDatabaseStuff() 
    } 

    @Transactional 
    public void doDatabaseStuff() { 
     ... 
    } 

    public void doNonDatabaseStuff() { 
     ... 
    } 
} 
+0

거래 관리자는 어디에 있습니까? 자세한 내용을 추가 할 수 있습니까? – Chris

+0

클래스 DoStuff가 인터페이스를 구현합니까? –

+0

인터페이스가없고 트랜잭션 관리자는 원래 클래스에서 작동하는 것으로 정의됩니다. –

답변

6

편집에 리팩토링이다

당신은 당신의 리팩토링이 작동하지 않는 이유를 이해하기 how Spring proxying works을 이해할 필요가있다.

개체 참조에 대한 메서드 호출은 프록시에서 호출되므로 해당 프록시는 해당 메서드 호출과 관련된 모든 인터셉터 (조언)에 위임 할 수 있습니다. 그러나 호출이 대상 객체에 도달하면 메소드 자체가 호출 할 수있는 메소드 호출이 프록시가 아닌이 참조에 대해 호출됩니다. 이것은 중요한 의미를 갖는다. 이는 자체 호출이 메소드 호출과 관련된 권고가 실행될 기회를 얻지 않을 것임을 의미합니다.

@Transactional은 Spring AOP를 사용하고, Spring은 프록시를 사용합니다. 즉, 다른 클래스에서 @Transactional 메소드를 호출하면 Spring은 프록시를 사용하므로 트랜잭션 조언이 적용됩니다. 그러나 동일한 클래스에서 메서드를 호출하면 스프링이 프록시 대신 "this"참조를 사용하므로 트랜잭션 조언이 적용되지 않습니다.

원래 답 : 여기

비슷한 시나리오에서 나를 위해 일한 것입니다.

public class DoStuff implement ApplicationContextAware {  
private ApplicationContext CONTEXT; 
public void setApplicationContext(ApplicationContext context) throws BeansException { 
    CONTEXT = context; 
} 

    public void originalMethod() {   
     getSpringProxy().doDatabaseStuff()    
     doNonDatabaseStuff()  
    } 

    private DoStuff getSpringProxy() { 
     return context.getBean(this.getClass());  
    } 
    @Transactional  
    public void doDatabaseStuff() {   
     ...  
    }   

    public void doNonDatabaseStuff() {   
     ...  
    } 
} 

설명 : 컨텍스트에 대한 참조를 가지고 있도록

  1. 클래스 ApplicationContextAware을 확인
  2. 당신은 트랜잭션 메소드를 호출 문맥에서 실제 스프링 프록시를 가져 오기 위해 필요로 할 때
  3. 이 프록시를 사용하여 메서드를 호출하면 @Transactional이 실제로 적용됩니다.
+0

원래 클래스는 @Transactional을 이미 인식하고 올바르게 작동합니다. 리팩토링 후에 만 ​​작동을 멈 춥니 다. –

+0

@Michael Rutherfurd 내 편집보기 – gresdiplitude

+0

이것은 내 문제인 것 같습니다. 감사합니다 –

0

당신의 접근 방식은 정상적으로 작동하는 것처럼 보입니다. 문제는 스프링 프록시와 관련이 있습니다.

내가 인터페이스에 대해 묻는 이유는 Spring이 트랜잭션 동작 (JDK 동적 프록시)을 적용하는 기본 메소드와 관련이 있기 때문입니다.

클래스의 실제 정의는 경우

public class DoStuff implements Doable { 
    public void originalMethod() { 

    } 
} 

public interface Doable { 
    public void originalMethod(); 
} 

이 새 구조 봄로 이동하는 경우, 실제로 구조 프록시에 새로운 doDatabaseStuff 방법 할 수없는 경우.

옵션은이 문제를 해결하려면 :

  • 이 인터페이스에 새로운 방법을 추가하기 위해 그 봄 할 수있는 프록시 그들을
  • CGLIB 기반의 프록시를 사용하는
  • 이동 (이러한 인터페이스에 의존하지 않는)
+0

아니 클래스는 POJO, 아니 전혀 구현 된 인터페이스 –

+0

오 잘. 아직도. 그것은 모두 사실입니다 :) –

+0

나는 동의하지 않았다. (단지이 경우에 적용되지 않는다고 말하는 것이었다 :-) –