簡體   English   中英

防止JBoss + Hibernate中的事務回滾

[英]Prevent transaction rollback in JBoss + Hibernate

我們有一個在JBoss 5.1上運行的Java應用程序,在某些情況下,我們需要防止在某些基礎方法拋出JDBCException情況下關閉事務。

我們有一個類似於下面的EJB方法

@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;
    }
  }
}

如果insertPersistenceException (包裝因違反約束而導致的JDBCException而失敗,我們希望在同一事務中繼續執行load

我們現在無法執行此操作,因為事務已由容器關閉。 這是我們在日志中看到的內容:

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類標有以下注釋

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

是否有任何正確的方法可以阻止事務在這種特定情況下回滾?

你真的不應該試着這樣做。 正如另一個答案中所提到的,並且引用了Hibernate Docs,Hibernate拋出的異常應該被視為可恢復的。 這可能會導致一些難以找到/調試的問題,尤其是使用hibernate自動臟檢查。

解決此問題的一種干凈方法是在插入對象之前檢查這些約束。 使用查詢來檢查是否違反了數據庫約束。

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

我知道這看起來有點痛苦,但這是你能確定哪個約束被違反的唯一方法(你無法從Hibernate例外中獲取這些信息)。 我相信這是最干凈的解決方案(至少最安全)。


如果您仍想從異常中恢復,則必須對代碼進行一些修改。

如上所述,您可以手動管理事務,但我不建議這樣做。 JTA API非常麻煩。 此外,如果您使用Bean管理事務(BMT),則必須為EJB中的每個方法手動創建事務,它是全部或全部。

另一方面,您可以重構您的方法,以便容器為您的查詢使用不同的事務。 像這樣的東西:

@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;
}

當你調用foo()時,當前事務(T1)將被掛起,容器將創建一個新事務(T2)。 發生錯誤時,將回滾T2,並恢復T1。 調用load()時,它將使用T1(仍處於活動狀態)。

希望這可以幫助!

我不認為這是可能的。

In可能依賴於您的JPA提供程序,但是,例如,Hibernate明確聲明任何異常都會使會話處於不一致狀態,因此不應被視為可恢復( 13.2.3。異常處理 )。

我想你能做的最好的事情就是禁用這種方法的自動事務管理,並在手動異常后創建一個新的事務(據我記憶,使用UserTransaction )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM