2011-10-25 38 views
6

Tôi đang có điều kỳ lạ xảy ra và tôi không thể hiểu tại sao. Cách tốt nhất để mô tả này là cung cấp một ví dụ đơn giản:Các trường hợp ngoại lệ được phát hiện sau khi đã bị bắt

@Service 
@Transactional 
public class Foo{ 
    public ModelAndView delete(@ModelAttribute("abc") Long id) { 
     ModelAndView mav = new ModelAndView(); 
     try { 
      getDaoService().delete(id); //Calls Bar.delete() 
     } catch (final Exception e) { 
      // Add a custom error message to the mav for the user to see 
      mav.getModelMap().addAttribute(blah, blah); 
     } 
     return mav; 
    } 
} 

@Service 
@Transactional 
public class Bar { 
    public void delete(final E entity) throws HibernateException { 
     if (null != entity) { 
      try { 
       sessionFactory.getCurrentSession().delete(entity); 
      } finally { 
       sessionFactory.getCurrentSession().flush(); 
      } 
     } 
    } 
} 

Trong trường hợp đặc biệt này, tôi đang cố gắng để xóa một đối tượng trong đó có một vi phạm chế (ORA-02.292). Tôi hy vọng xóa để thất bại vì điều này. Khi xóa không thành công, tôi muốn hiển thị cho người dùng một thông báo tùy chỉnh thích hợp.

Thay vì có thể hiển thị cho người dùng một thông báo tùy chỉnh, cuộc gọi bị lỗi và hiển thị sau để màn hình:

org.springframework.transaction.UnexpectedRollbackException: Giao dịch cuộn lại bởi vì nó đã được đánh dấu dưới dạng rollback-only

Khi tôi sử dụng trình gỡ lỗi, tôi có thể thấy lỗi bị bắt và đối tượng ModelAndView có thông điệp tùy chỉnh bên trong. Vì vậy, tôi không có đầu mối tại sao một ngoại lệ vẫn còn bị ném sau khi nó đã bị bắt và xử lý. Có ai có cái nhìn sâu sắc về lý do tại sao điều này đang xảy ra?

Trả lời

5

Trên chú thích @Transactional, bạn có thể nêu có hay không quay lại giao dịch của mình do một ngoại lệ nhất định sử dụng thuộc tính noRollbackForClassName. Bạn có thể làm điều đó tương tự như thế này.

@Service 
@Transactional(noRollbackForClassName = "java.lang.Exception") 
public class YourClass { 
    ... 
} 

Tuy nhiên, lưu ý rằng chỉ nói noRollbackForClassName = "java.lang.Exception" có nghĩa là nó sẽ không rollback cho bất kỳ ngoại lệ (hoặc lớp con của nó), do đó không phải là một thực hành tốt của nó.

Điều bạn nên làm là, tìm ra ngoại lệ nào thực sự được ném trước (có thể bằng cách in ra e.getClass().getName()), sau đó đặt tên lớp đó làm giá trị noRollbackForClassName. Lý do, điều này xảy ra bởi vì nếu một số ngoại lệ được ném trong khi cố xóa(), giao dịch hiện tại sẽ tự động được đánh dấu là chỉ quay lại và nếu nó được cố gắng thực hiện, ngoại lệ bạn thấy sẽ bị ném . Cách để vượt qua điều này là để tuyên bố rõ ràng rằng ngoại lệ nhất định này không nên gây ra một cuộn trở lại.

+0

Tuyệt vời, làm việc như một sự quyến rũ! Cảm ơn!!! – user973479

1

Ngoại lệ được ném trong Bar # delete và bị xóa trong Foo # delete. Có một chú thích @Transactional trên Bar # delete được vượt qua trước khi ngoại lệ bị bắt. Giao dịch bên trong này đang tham gia vào giao dịch bên ngoài và do đó toàn bộ giao dịch được đánh dấu để khôi phục.

Để tránh điều này, bạn có thể xóa chú thích @Transactional cho Thanh # xóa. Phương thức này đã được gọi trong phạm vi của giao dịch khác.

+0

Tuyệt vời, làm việc như một sự quyến rũ! Cảm ơn!!! – user973479

4

Vấn đề là do một khi ngoại lệ được ném, Spring đánh dấu nội bộ tx là chỉ rollback. Điều này hoàn toàn tách biệt với việc xử lý ngoại lệ Java. Bạn có một số tùy chọn:

  • Đảm bảo ngoại lệ mong đợi của bạn không ném ngoại lệ mở rộng RuntimeException; Mùa xuân chỉ cuộn lại của tx khi một loại RuntimeException (see this page, phần 10.5.3). HibernateException extends RuntimeException, vì vậy đó là lý do bạn nhận được điểm đánh dấu quay vòng.
  • Chạy từng tx trong giao dịch của chính nó bằng cách di chuyển phương thức giao dịch đến lớp của riêng nó và chú thích nó bằng cách sử dụng @Transactional(propagation=Propagation.REQUIRES_NEW).Sau đó, mỗi cuộc gọi sẽ chạy trong tx của riêng nó và sẽ không ảnh hưởng đến tx tổng thể.
  • Sử dụng venubka kiểu noRollbackForClassName được đề cập. Nhưng sử dụng cẩn thận, vì lý do được đề cập.
+0

Vậy tôi có thể làm gì khi ngoại lệ mở rộng RuntimeException? – sebnukem

0

Thêm thuộc tính "globalRollbackOnParticipationFailure" vào định nghĩa bean hibernateTransactionManager như sau.

<bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
    <property name="sessionFactory" ref="hibernateSessionFactory" /> 
    **<property name="globalRollbackOnParticipationFailure" value="false" />** 
</bean> 
Các vấn đề liên quan