2012-07-20 123 views
3

我們正在開發一個項目,JaveEE6訪問@Lob場,使用EJB3豆類和JPA2註解。如何從一個EJB3擴​​展持久化上下文

我們有一些有狀態的EJB3 bean,它們使用擴展持久性上下文,以便將數據庫實體顯示在前面(通過某些接口,不需要DTO)。

典型的使用是一樣的東西:

  • 所有的方法都是沒有交易,因爲我們不希望立即通過非事務性方法提交用戶修改
  • ,我們正在裝載實體,附加到擴展上下文
  • 只有一種保存方法是事務性的:在檢查用戶數據之後,實體被提交併保存在數據庫中。

與MySQL數據庫,一切都運行得很好。

唉,Postgres,在非事務性方法中加載的@Lob字段出現問題。 JDBC似乎禁止交易外高吊球訪問,拋出一個: org.hibernate.exception.GenericJDBCException: Large Objects may not be used in auto-commit mode


作爲stackoverflower pointed,一個LOB可以在多個記錄,所以它需要一個事務保持一致。

autocommit設置爲true persistence.xml根本不起作用,還有should not be done

我不能使方法事務性,因爲我們不想在調用結束時提交任何內容。那麼,有誰知道我怎麼才能簡單地訪問Lob?

我們想象的一個黑客解決方案是將Lob移動到另一個實體中,然後添加一個讀取Lob內容的事務方法,以便我們可以使用它。很骯髒,我認爲......

回答

0

由於某些體系結構原因,我們選擇在另一個Entity中設置Lob字段,並通過@Stateless bean讀取/寫入該字段。

實體:

@Entity 
@Access(AccessType.FIELD) 
public class LobEntity 
{ 
    [...] 

    @Lob 
    private String content; 

    public String getContent() 
    { 
     return content; 
    } 

    public void setContent(String content) 
    { 
     this.content = content; 
    } 
} 

服務:

@Stateless 
@LocalBean 
public class LobService 
{ 
    @PersistenceContext 
    private EntityManager em; 

    public String readLob(Long lobId) 
    { 
     LobEntity lobEntity = em.find(LobEntity.class, lobId); 
     return lobEntity.getContent(); 
    } 

    public LobEntity writeNewLob(String content) 
    { 
     LobEntity lob = new LobEntity(content); 
     em.persist(lob); 
     return lob; 
    } 
} 

這直接包含高球一類,但沒有更多:

@Entity 
@Access(value = AccessType.FIELD) 
public class MyEntity 
{ 
    [...] 

    protected Long contentLobId; 
    @Transient 
    protected String editableContent; 

    public Long getContentLobId() 
    { 
     return contentLobId; 
    } 

    public void setContentLobId(Long contentLobId) 
    { 
     this.contentLobId = contentLobId; 
    } 

    public String getEditableContent() 
    { 
     return editableContent; 
    } 

    public void setEditableContent(String editableContent) 
    { 
     this.editableContent = editableContent; 
    } 
} 

實體不具備LobEntity本身,以避免開發人員嘗試像傻瓜一樣訪問它:如果我們想要內容在非事務性上下文中,我們使用的是LobService。當我們想保存編輯的內容時,我們也使用相同的bean。

0

嘗試getEntityManager().flush();

此寫入數據庫,但不提交當前事務。假設你的隔離級別是「讀取提交」,那麼在你實際提交事務之前,你不會在其他查詢中看到更新。請記住,您將在所觸摸的行上持有鎖...

2

您似乎覺得對JPA上下文中加載的實體的更改會自動提交,除非該實體已分離。這是而不是,實際上它顯然是如何工作的。但是,即使您修改附加實體並刷新,或者合併了分離的實體,也可以確保其他事務永遠不會看到更改。

它是無害的 - 而且往往一致性是一個好主意 - 執行只讀操作,當這麼久有交易開放只要你不把它開得太久 。如果你想保證沒有數據被寫入,並且你正在使用JTA,只需使用SessionContext上的setRollbackOnly()來確定它。對於手動JPA事務管理,請確保您在完成後致電EntityTransaction上的rollback(),而不是提交。

個人而言,我會建議你「getLob」方法使用一個新的事務,並回滾在方法結束。如果您的數據庫不支持嵌套事務(很少做),這通常會導致從池中獲取新連接以執行此項工作。

如果你使用JTA和容器管理的事務,請嘗試:

@Stateless 
@TransactionManagement(TransactionManagementType.CONTAINER) 
public class LobTest { 

    @PersistenceContext 
    private EntityManager em; 

    @Resource 
    private SessionContext sctx; 

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public byte[] getLob() { 
     // Get your LOB content by fetching a new copy of the entity from the DB 
     // by ID, avoiding the need to split the LOB out. Note that you lose 
     // tx consistency guarantees between the LOB and the rest of the entity by 
     // doing this. 
     // then after loading the LOB: 
     sctx.setRollbackOnly(); 
    } 

} 

另外,如果你不介意的錯誤讀取LOB中止任何周圍的事務,使用TransactionAttributeType.REQUIRES代替REQUIRES_NEW和Don」 t setRollbackOnly()。你不能改變任何東西,所以沒有什麼會被提交。如果尚未打開,它將打開一個新事務,否則將加入現有事務,以便您可以一致地讀取LOB。唯一的缺點是一些數據庫錯誤會中止整個JTA事務。

如果您使用用戶管理的事務與非JTA環境中,僅僅獲得一個新的EntityManager,得到一個EntityTransaction,使用em.find(...)加載包含實體LOB的新副本等


。好的,所以在大多數數據庫中都有一些不需要事務處理的對象類型,比如PostgreSQL SEQUENCE和相關的SERIAL僞類型,諮詢鎖等等,即使回滾的事務也會受到這種類型的影響。事務也可以「鎖定」數據庫,從而鎖定可能阻止其他操作的資源。對於實際的數據,這是安全的。

。如果可以的話,避免讓tx保持打開狀態的時間超過幾秒鐘,因爲長時間運行的事務會導致某些數據庫出現性能問題,並且會阻塞連接池。避免在「用戶的思考時間」內保持交易 - 當你等待用戶做某件事時 - 他們可能會做白日夢,午餐,度假或月球......讓你的窮人數據庫和連接池等待他們的回報。

+0

這是一個非常類似於我們想過的解決方案(添加一個讀取Lob內容的事務方法,以便我們可以使用它)。 我認爲如果沒有奇蹟發生,我們會爲之努力,因爲它是我們能找到的最好的。 – 2012-07-25 08:05:33

+0

@XavierPortebois可惜的是PgJDBC無法讓你獲得整個LOB的更透明,從而關注幕後的事務管理。也許你應該看看PgJDBC的來源?畢竟它是開源的,你可能會加強它以適應你的需求。 – 2012-07-25 13:53:43

+1

哇。在寫這篇文章的時候,我發現JTA bean管理的事務沒有相同的'REQUIRES_NEW'。他們不能暫停和恢復交易。如果您希望您使用*的功能來使用容器管理的事務。 – 2012-07-26 00:00:27

相關問題