2013-10-29 25 views
3

我有一種方法接收JPA Entity及其相關的EntityManager作爲參數。 Entity實例不是在類內部創建的,它很可能被其他類共享(如GUI等)。JPA:回滾後刷新分離的實體並重新附加它

該方法啓動一個事務,對實體進行一些更改,並最終提交事務。

如果提交失敗,則調用EntityTransaction.rollback():根據JPA規範,實體將從管理器中分離。

在故障情況下的應用需要放棄掛起的更改,恢復實體e內的原始值,並將其重新連接到EntityManager,這樣對e對象的各個分散的引用將繼續有效。這個問題引起了這裏:我的理解是,這是不使用EntityManager的API來簡單的操作:由於e被分離

  • 調用EntityManager.refresh(e)是不可能的。
  • e = EntityManager.merge(e)會爲e創建一個新實例:運行時程序中對原始e的所有其他引用都不會更新爲新實例。這是主要問題。此外(實際上對此不太確定),EntityManager.merge(e)將用當前由e保持的值(即,可能導致提交失敗的值)來更新新的託管實例的值。相反,我需要的是重置它們。

示例代碼:

public void method(EntityManager em, Entity e) { 
    EntityTransaction et = em.getTransaction(); 
    et.begin(); 
    ... 
    // apply some modifications to the entity's fields 
    ... 
    try { 
     et.commit(); 
    } catch (Exception e) { 
     et.rollback(); 

     // now that 'e' is detached from the EntityManager, how can I: 
     // - refresh 'e', discarding all pending changes 
     // - without instantiating a copy (i.e. without using merge()) 
     // - reattach it 
    } 
} 

什麼是在這種情況下,最好的辦法?

+0

是否給定的實體'e'有任何變化,這是不將提交給數據庫了嗎? – Uooo

+0

該實體需要進行哪些其他操作,以便您需要遵循對其的引用?解決這種情況的一個基本方法是創建一個幹運行以確保不會發生回滾。另一個基本的方法是創建一個子交易,基本上克隆實體網絡以作爲預期回滾的承諾。 –

+0

@Uooo:不,該方法假定'e'的狀態在作爲參數傳遞時與數據庫行同步。 @BobDalgleish:'e'對象本身可能被其他(未知)類引用,它們可以在不同的事務中分別修改它的其他字段(儘管不是併發)。我希望存在一些非複雜的重置和重新連接策略。你的方法也很有用。 –

回答

0

一個可能的解決辦法是這樣的:

public class YourClass { 
    private EntityManager em = ...; // local entity manager 

    public void method(Entity e) { // only entity given here 
     Entity localEntity = em.find(Entity.class, e.getId()); 
     EntityTransaction et = em.getTransaction(); 
     et.begin(); 
     ... 
     // apply some modifications to the local entity's fields 
     applyChanges(localEntity); 
     ... 
     try { 
      et.commit(); 
      // Changes were successfully commited to the database. Also apply 
      // the changes on the original entity, so they are visible in GUI. 
      applyChanges(e); 
     } catch (Exception ex) { 
      et.rollback(); 
      // original entity e remains unchanged 
     } 
    } 

    private void applyChanges(Entity e) { 
     ... 
    } 
}