2011-07-07 146 views
0

以下代碼無法可靠地創建新記錄。EntityManager.merge()不創建表記錄

TransferRecord transfer = new TransferRecord(); 
transfer.setTransferId(metaData.getTransferId()); 
transfer.setUserName(metaData.getUserId().getUserName()); 
transfer.setCancelled(false); 
em.merge(transfer); 
em.flush(); 

下面是轉移記錄代碼:

/*** 
* Contains a record of an ongoing transfer. 
* @author anchapma 
* 
*/ 
@Entity 
@Table(name = "TransferRecord") 
public class TransferRecord implements Serializable 
{ 
    /** 
    * Generated static unique ID. 
    */ 
    private static final long serialVersionUID = -8131586518447878482L; 

    /*** 
    * The id of the transfer. 
    */ 
    private String transferId; 

    /*** 
    * The name of the user who ownes the transfer. 
    */ 
    private String username; 

    /*** 
    * Has this transfer been cancelled? 
    */ 
    private boolean cancelled; 

    /*** 
    * Constructor. 
    */ 
    public TransferRecord() 
    { 
     this.transferId = ""; 
     this.username = ""; 
     this.cancelled = false; 
    } 

    /*** 
    * Gets the transfer ID. 
    * 
    * @return The transfer ID. 
    */ 
    @Id 
    @Column(name = "TransferID", unique = true, nullable = false, length = 50) 
    public String getTransferId() 
    { 
     return this.transferId; 
    } 

    /*** 
    * Sets the transfer ID. 
    * @param transferId The new transfer ID. 
    */ 
    public void setTransferId(String transferId) 
    { 
     this.transferId = transferId; 
    } 

    /*** 
    * Gets the username. 
    * 
    * @return The username. 
    */ 
    @Column(name = "UserName", nullable = false, length = 50) 
    public String getUserName() 
    { 
     return this.username; 
    } 

    /*** 
    * Sets the username. 
    * @param username The new username. 
    */ 
    public void setUserName(String username) 
    { 
     this.username = username; 
    } 

    /*** 
    * Gets whether or not the transfer has been cancelled. 
    * 
    * @return True if the transfer has been cancelled. 
    */ 
    @Column(name = "Cancelled", nullable = false, length = 50) 
    public boolean getCancelled() 
    { 
     return this.cancelled; 
    } 

    /*** 
    * Sets whether or not the transfer has been cancelled. 
    * @param cancelled True if the transfer has been cancelled. 
    */ 
    public void setCancelled(boolean cancelled) 
    { 
     this.cancelled = cancelled; 
    } 
} 

我認爲,正在發生的事情是一條記錄被延遲一段時間後加入到數據庫中。我的代碼使用TransferRecord記錄的存在或不存在作爲標誌。因此它需要數據立即顯示在表格中。

我可能是正確的我的假設?如果是這樣,有沒有辦法強制em.flush()調用等待,直到它返回前寫出記錄?

+0

你的一些陳述非常模糊。例如,「因此它需要數據立即顯示在表格中。」從交易的角度來看沒什麼意義。 'em.flush()'將導致持久化上下文被刷新到數據庫;不同事務中這些更改的可見性受事務隔離級別的限制。因此,說出你想要的行爲是值得的。另外,「有沒有辦法強制em.flush()調用等到它在返回之前寫出記錄?」沒有任何意義,因爲它是一個阻塞呼叫。 –

+0

你正在使用什麼樣的轉換,以及檢查「TransferRecord記錄是否存在」的代碼在哪裏運行? – ddewaele

+0

代碼在由另一個bean調用的bean中運行。調用bean是無狀態的並且執行很多秒。在bean的操作結束之前,數據不會顯示給其他數據庫用戶。 –

回答

1

該問題時的一篇評論 -

「主叫豆是無狀態的,並執行許多秒的數據不顯示其他數據庫用戶,直到bean的操作結束。」

此描述的行爲與刷新與EntityManager關聯的持久性上下文無關。它與事務相關的事務隔離級別有很大關係。與無狀態會話Bean(SLSB)相關聯的事務實際上僅在從bean方法返回時向數據庫提交數據,因爲每個SLSB方法可能與REQUIRED的默認事務屬性(其使用現有事務或開始新事務一);最終的行爲是通過SLSB方法中的回滾或通過從方法返回時的提交來終止事務。

此行爲會影響其他客戶端和事務執行的讀取,因爲結果的結果取決於當前相關事務的隔離級別,而這又取決於數據庫連接池上指定的隔離級別。對於大多數數據庫和關聯的連接池,事務隔離級別恰好爲READ COMMITTED,因此其他事務只能讀取由相關事務(即SLSB方法返回)提交的數據。在大多數情況下,這是可取的行爲,因爲您不希望讀取未提交的數據(並且稍後可能會回滾),從而導致髒讀的可能性。

如果您打算執行髒讀操作,則必須將JDBC連接池配置爲允許髒讀,或者換句話說,將池的事務隔離級別設置爲READ UNCOMMITTED。配置變化因容器而異,並且也受到數據庫支持的隔離級別的限制;例如,Oracle不允許將隔離級別設置爲READ UNCOMMITTED,並可能將其設置爲它所支持的下一個更高的隔離級別(READ COMMITTED)。

如果您想避免使用事務隔離級別,請考慮將方法調用拆分爲多個事務。您可以通過在單獨的SLSB中創建新的業務方法來實現此目的,這需要在每次調用時創建一個新的事務(REQUIRES_NEW)。僞代碼示例概述如下:

@Stateless 
@Local(X.class) 
public class SLSBX implements X { 

    @EJB Y y; 

    @TransactionAttribute(TransactionAttributeType.REQUIRED) // start a new transaction to do work 
    public void sampleMethod() { 
     // suspends the existing transaction on invoking Y.anotherSampleMethod() 
     Y.anotherSampleMethod(); 
     // continue doing work in the current transaction. Work done in anotherSampleMethod() would have either committed or rolled back. 
    } 
} 

@Stateless 
@Local(Y.class) 
public class SLSBY implements Y { 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) // Suspends the existing transaction and creates a new transaction that terminates on method return 
    public void anotherSampleMethod() { 
     // do some stuff 
    } 
} 

這種做法當然,僅建議如果交易的業務性質允許採用這種做法。通常,必須將屬於業務交易一部分的所有業務活動都包含在實際交易中。