2013-04-23 3 views
4

롤백이 작동하지 않는 것으로 보이는 스프링 트랜잭션 롤백 문제가 있습니다.
@Transactional 주석이 달린 서비스 레이어 메서드 내에서 나는 세 개의 다른 DAOImpl 클래스를 호출하여 3 개의 레코드를 삽입합니다.
중간 삽입은 네 번째 테이블에서 get을 수행하여 설명 필드를 채우지 만 실패합니다. 첫 번째 삽입물이 롤백 될 것으로 예상되지만 실제로 발생하지는 않습니다.
몇 가지 포인트 :스프링 트랜잭션이 롤백되지 않습니다.

  1. '가져 오기'방법 우리는 applicationContext.xml에 정의 org.springframework.jdbc.datasource.DataSourceTransactionManagerMySQL datasource를 사용하는 런타임 예외
  2. 에게 던졌습니다. 콩이 Beans.xml에서 만든 우리는 우리는

UPDATE을 봄 3.1을 사용하는 applicationContext.xml

  • 다시 <tx:annotation-driven transaction-manager="transactionManager"/>을 사용했다
  • DAO 계층에 ApplicationContext.xml
  • 없음 @Transactional 주석으로 수입되는 :

    코드 스 니펫 ....

    서비스 클래스 - 이것은 내가 가지고있는 것과 비슷한 것입니다. 나는 @Autowired가 있는지 여부에 관계없이 테스트를 마쳤습니다. 트랜잭션 활성화 메소드는 서비스 클래스 내에서 호출됩니다.

     
    public class CustomerService { 
    
        //@Autowired 
        CustomerOrderDAO customerOrderDAOImpl; 
        //@Autowired 
        CustomerItemDAO customerItemDAOImpl; 
        //@Autowired 
        CustomerPromotionDAO customerPromotionDAOImpl; 
        //@Autowired 
        PromotionDAO promotionDAOImpl; 
    
        //other variables 
    
    
        public CustomerOrder handleIncomingOrders(CustomerOrder customerOrder) { 
         try { 
          saveOrderDetails(customerOrder); 
          ..... 
          return customerOrder; 
         } catch (Exception e) //TO-DO catch proper exception 
         { 
          //Send error response 
          ....... 
          return customerOrder; 
         } 
        } 
    
        @Transactional 
        public void saveOrderDetails(CustomerOrder customerOrder) throws Exception { 
          customerOrderDAOImpl.create(customerOrder); 
          .... 
          while (promotionsIterator.hasNext()) { 
           customerPromotion.setPromotionName(promotionDAOImpl.getName(customerOrder.getPromotionId)); 
           customerPromotionDAOImpl.create(customerPromotion); 
          } 
          ...... 
          while (customerItemIterator.hasNext()) { 
           customerItemDAOImpl.create(customerItem); 
          } 
    
        } 
    } 
    

    어떤 생각? 감사합니다. .

  • +3

    서비스 코드를 부르는 방법과 서비스 방법을 보여줄 수 있습니까? 서비스 내에서 또는 외부에서? 또는 당신이 직접 쓴 캐치를 시도합니까? –

    +0

    트랜잭션 정의에 전파가 설정되어 있습니까 –

    +0

    '자동 커밋 '모드로 연결되어 있습니까? – kan

    답변

    3

    @Transactional의 기본 동작은 개체 주위에 프록시 (예 : CustomerService)와 함께 트랜잭션 동작이 추가된다는 것입니다. reference docs에서 (아래로 스크롤) :

    을 프록시 모드에서 (디폴트) 만 외부 방법은 프록시를 통해 들어오는 것은 차단되어 호출합니다. 즉, 대상 객체의 다른 메소드를 호출하는 대상 객체 내의 메소드 자체 호출은 호출 된 메소드가 @Transactional로 표시되어 있더라도 런타임시 실제 트랜잭션으로 이어지지 않습니다. 하여 예에서

    handlingIncomingOrders()에 대한 외부 호출 프록시를 통과하여 대상 객체 (CustomerService의 인스턴스)를 히트. 그러나 saveOrderDetails()에 대한 후속 호출은 대상 개체 내부의 일반적인 메서드 호출이므로 프록시의 트랜잭션 동작이 호출되지 않습니다. 그러나 saveOrderDetails()이 다른 클래스에서 호출 된 경우 트랜잭션 동작이 예상대로 작동 함을 알 수 있습니다.

    +0

    감사. 그것은 효과가 있었다. 그러나, 나는 그것이 조금 불편한 것을 안다. 기본 프록시 모드를 어떻게 변경할 수 있습니까? – Ish

    +0

    대답은''AspectJ 모드 (아래 표에서 mode 속성 참조)를 사용하면 자체 호출이 트랜잭션으로 랩핑되기를 기대할 수 있습니다.이 경우 처음에는 프록시가 없을 것입니다 대신에, @Transactional을 모든 종류의 메소드에서 런타임 동작으로 바꾸기 위해 대상 클래스가 만들어집니다 (즉, 바이트 코드가 수정 될 것입니다). " – Ish

    +0

    @Ish 나는 일반적으로 트랜잭션 경계를 명확하게 정의합니다. 클래스 레벨에서'@ Transactional' 주석을 사용하고 또한 타겟 객체를 호출하는 (public) 메소드를 회피하거나 리팩토링하여 트랜잭션을 명시 적으로 처리합니다. – matsev

    1

    케이스의 해결책은 saveOrderDetails(customerOrder);proxyBean.saveOrderDetails(customerOrder);으로, proxybean is the Object on which handleIncomingOrders`을 (를) 호출하는 것입니다.

    CustomerServicesingleton (Defualt 범위) 인 경우 다음 코드를 Service 클래스에 추가하는 것만 큼 간단 할 수 있습니다.그 범위는 다음과 같이 될 것이다 가능한 단순 용액 Prototype 하나 인 경우 (자동 설정할 같이 자기 기준 추가)

    //@Autowired 
    CustomerService customerService; // As this is injected its a proxy 
    

    상기 방법에

    public CustomerOrder handleIncomingOrders(CustomerOrder customerOrder) { 
        try { 
         customerService.saveOrderDetails(customerOrder); 
         ..... 
         return customerOrder; 
        } catch (Exception e) //TO-DO catch proper exception 
        { 
         //Send error response 
         ....... 
         return customerOrder; 
        } 
        } 
    

    로 사용.

    public CustomerOrder handleIncomingOrders(CustomerOrder customerOrder, CustomerService customerService) { 
        try { 
         customerService.saveOrderDetails(customerOrder); 
         ..... 
         return customerOrder; 
        } catch (Exception e) //TO-DO catch proper exception 
        { 
         //Send error response 
         ....... 
         return customerOrder; 
        } 
        } 
    

    어디 당신이 코드 아래에 제안 handleIncomingOrders 이용 변화를 요구하고있다.

    bean.handleIncomingOrders(customerOrder); //Suppose this is old code 
    Change it to 
        bean.handleIncomingOrders(customerOrder, bean);// THough it appears as we are sending reference to `THIS` as parameter whcihc can be unnecessary, in case of `Proxy`while inside your method `this` and `Passed reference` will point to different Obejects. 
    
    관련 문제