2013-04-02 60 views
17

Đây là vấn đề của tôi:hành vi kỳ lạ với @Transactional (tuyên truyền = Propagation.REQUIRES_NEW)

Tôi đang chạy một lô trên một ứng dụng Java EE/Spring/Hibernate. Lô này gọi method1. Phương thức này gọi là method2 có thể ném UserException (một lớp mở rộng RuntimeException). Sau đây là cách nó trông giống như:

@Transactional 
public class BatchService implements IBatchService { 
@Transactional(propagation=Propagation.REQUIRES_NEW) 
public User method2(User user) { 
    // Processing, which can throw a RuntimeException 
} 

public void method1() { 
    // ... 
    try { 
    this.method2(user); 
    } catch (UserException e) { 
    // ... 
    } 
    // ... 
} 
} 

Các ngoại lệ được đánh bắt như việc thực hiện vẫn tiếp tục, nhưng ở phần cuối của method1 khi giao dịch được đóng một RollbackException được ném.

Đây là stack trace:

org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly 
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:476) 
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754) 
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723) 
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393) 
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) 
at $Proxy128.method1(Unknown Source) 
at batch.BatchController.method1(BatchController.java:202) 

Khi method2 không ném ngoại lệ này, nó hoạt động tốt.

Những gì tôi đã cố gắng:

  • Thiết @Transactional(noRollbackFor={UserException.class})) trên method1
  • Hãy thử và đánh bắt trong method2

Nhưng nó không thay đổi bất cứ điều gì.

Vì ngoại lệ được đưa vào một giao dịch khác, trong đó một trường hợp hoàn tiền xảy ra, tôi không hiểu tại sao nó không hoạt động. Tôi đã xem xét điều này: Jpa transaction javax.persistence.RollbackException: Transaction marked as rollbackOnly nhưng nó đã không thực sự giúp tôi.

Tôi sẽ rất tuyệt vời nếu ai đó có thể cho tôi một đầu mối.

Cập nhật

Tôi đã thực hiện nó hoạt động bằng cách thiết lập propagation=Propagation.REQUIRES_NEW vào phương pháp gọi bằng method2 (mà thực sự là một trong đó là gửi các ngoại lệ). Phương pháp này được định nghĩa trong một lớp rất giống với BatchService của tôi. Vì vậy, tôi không thấy lý do tại sao nó hoạt động trên cấp độ này và không phải trên method2.

  • tôi đã thiết method2 như nào là chú thích @Transactional không đưa vào tài khoản nếu phương pháp này là tin như đã nói trong tài liệu:

Chú thích @Transactional có thể được đặt trước một giao diện định nghĩa, một phương thức trên giao diện, định nghĩa lớp hoặc phương thức công khai trên một lớp.

  • Tôi cũng cố gắng sử dụng Exception thay vì RuntimeException (vì nó là thích hợp hơn) nhưng nó cũng không thay đổi bất cứ điều gì.

Ngay cả khi nó đang hoạt động, câu hỏi vẫn mở vì nó có hành vi lạ và tôi muốn hiểu tại sao nó không hoạt động như vậy.

+0

Xem http://stackoverflow.com/questions/5152686/self-injection-with-spring/ để giải quyết các vấn đề có thể xảy ra. – Vadzim

Trả lời

35

Giao dịch mùa xuân, theo mặc định, hoạt động bằng cách gói bean Spring với proxy xử lý giao dịch và ngoại lệ. Khi bạn gọi method2() từ method1(), bạn hoàn toàn bỏ qua proxy này, vì vậy, bạn không thể bắt đầu một giao dịch mới và bạn đang thực hiện một cách hiệu quả số method2() từ cùng một giao dịch với giao dịch được mở bởi cuộc gọi đến method1().

Ngược lại, khi bạn gọi phương thức của một bean được tiêm khác từ method1(), bạn đang thực tế gọi một phương thức trên proxy giao dịch. Vì vậy, nếu phương pháp ngoài hành tinh này được đánh dấu bằng REQUIRES_NEW, giao dịch mới được bắt đầu bởi proxy và bạn có thể bắt ngoại lệ trong method1() và tiếp tục giao dịch bên ngoài.

Điều này được mô tả trong the documentation.

+0

Câu trả lời hoàn hảo với sự hiểu biết thực sự về những gì đang xảy ra, Cảm ơn bạn! – DessDess

+2

Lưu ý rằng nếu bạn cần những lời tự gọi này để làm việc, bạn nên sử dụng chế độ AspectJ trong Spring. Xem http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html: "Hãy xem xét việc sử dụng chế độ AspectJ (xem thuộc tính chế độ trong bảng bên dưới) nếu bạn mong đợi tự viện dẫn Trong trường hợp này, sẽ không có một proxy ở nơi đầu tiên, thay vào đó, lớp đích sẽ được dệt (nghĩa là, mã byte của nó sẽ được sửa đổi) để biến @Transactional thành thời gian chạy hành vi trên bất kỳ loại phương pháp nào. " –

+0

vâng, hiểu biết thực sự về giao dịch mùa xuân ... cảm ơn bạn – Vito

Các vấn đề liên quan