2012-01-04 29 views
7

Chúng tôi có một ứng dụng Java chạy trên JBoss 5.1 và trong một số trường hợp, chúng ta cần ngăn chặn một giao dịch bị đóng trong trường hợp một số phương pháp cơ bản được đưa ra bởi JDBCException.Ngăn chặn giao dịch rollback trong JBoss + Hibernate

Chúng tôi có một phương pháp EJB trông giống như sau một

@PersistenceContext(unitName = "bar") 
public EntityManager em; 

public Object foo() { 
    try { 
    insert(stuff); 
    return stuff; 
    } (catch PersistenceException p) { 
    Object t = load(id); 
    if (t != null) { 
     find(t); 
     return t; 
    } 
    } 
} 

Nếu insert thất bại vì một PersistenceException (mà kết thúc tốt đẹp một JDBCException do vi phạm chế), chúng tôi muốn tiếp tục thực hiện với load trong cùng một giao dịch.

Chúng tôi không thể thực hiện ngay bây giờ vì giao dịch bị đóng bởi vùng chứa. Đây là những gì chúng ta thấy trong các bản ghi:

lớp
org.hibernate.exception.GenericJDBCException: Cannot open connection 
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Cannot open connection 
    at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614) 

    ... 

Caused by: javax.resource.ResourceException: Transaction is not active: tx=TransactionImple < ac, BasicAction: 7f000101:85fe:4f04679d:182 status: ActionStatus.ABORT_ONLY > 

EJB được đánh dấu bằng các chú thích sau

@Stateless 
@TransactionManagement(TransactionManagementType.CONTAINER) 
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 

Có cách nào thích hợp để ngăn chặn các giao dịch từ lăn trở lại chỉ trong trường hợp cụ thể này?

Trả lời

5

Bạn thực sự không nên cố gắng làm điều đó. Như đã đề cập trong một câu trả lời khác, và trích dẫn tài liệu Hibernate, không có ngoại lệ nào được ném bởi Hibernate nên được coi là có thể phục hồi. Điều này có thể dẫn bạn đến một số khó khăn để tìm/gỡ lỗi các vấn đề, đặc biệt với hibernate tự động kiểm tra bẩn.

Cách sạch để giải quyết vấn đề này là kiểm tra các ràng buộc đó trước khi chèn đối tượng. Sử dụng truy vấn để kiểm tra xem liệu ràng buộc cơ sở dữ liệu có bị vi phạm hay không.

public Object foo() { 
    if (!objectExists()) { 
     insertStuff(); 
     return stuff(); 
    } 
    // Code for loading object... 
} 

Tôi biết điều này có vẻ hơi đau, nhưng đó là cách duy nhất bạn biết chắc chắn ràng buộc nào bị vi phạm (bạn không thể lấy thông tin đó từ trường hợp ngoại lệ Hibernate). Tôi tin rằng đây là giải pháp sạch nhất (an toàn nhất, ít nhất).


Nếu bạn vẫn muốn khôi phục ngoại lệ, bạn phải thực hiện một số sửa đổi đối với mã của mình.

Như đã đề cập, bạn có thể quản lý các giao dịch theo cách thủ công, nhưng tôi không khuyến nghị điều đó. API JTA thực sự cồng kềnh. Bên cạnh đó, nếu bạn sử dụng Bean Managed Transaction (BMT), bạn phải tự tạo các giao dịch cho mọi phương thức trong EJB của bạn, tất cả hoặc không có gì.

Mặt khác, bạn có thể cấu trúc lại các phương thức của mình để vùng chứa sẽ sử dụng một giao dịch khác cho truy vấn của bạn.Một cái gì đó như thế này:

@Stateless 
public class Foo { 
    ... 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public Object foo() { 
     try { 
      entityManager.insert(stuff); 
      return stuff; 
     } catch (PersistenceException e) { 
      if (e.getCause() instanceof ConstraintViolationException) { 
       // At this point the transaction has been rolled-backed. 
       // Return null or use some other way to indicate a constrain 
       // violation 
       return null; 
      } 
      throw e; 
     } 
    } 

    // Method extracted from foo() for loading the object. 
    public Object load() { 
     ... 
    } 
} 

// On another EJB 
@EJB 
private Foo fooBean; 

public Object doSomething() { 
    Object foo = fooBean.insert(); 
    if (foo == null) { 
     return fooBean.load(); 
    } 

    return foo; 
} 

Khi bạn gọi foo(), giao dịch hiện tại (T1) sẽ bị tạm ngừng và vùng chứa sẽ tạo mới (T2). Khi lỗi xảy ra, T2 sẽ được khôi phục, và T1 sẽ được khôi phục. Khi load() được gọi, nó sẽ sử dụng T1 (vẫn hoạt động).

Hy vọng điều này sẽ hữu ích!

2

Tôi không nghĩ điều đó là có thể.

Trong có thể phụ thuộc vào nhà cung cấp JPA của bạn, nhưng, ví dụ, Hibernate tuyên bố rõ ràng rằng bất kỳ trường hợp ngoại lệ nào trong phiên không phù hợp và do đó không được coi là có thể phục hồi (13.2.3. Exception handling).

Tôi đoán điều tốt nhất bạn có thể làm là tắt quản lý giao dịch tự động cho phương pháp này và tạo giao dịch mới sau ngoại lệ theo cách thủ công (sử dụng UserTransaction, theo như tôi nhớ).

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