2011-07-07 9 views
0

次のコードは新しいレコードを確実に作成できません。私は何が起こっていることは、レコードが遅延した後、データベースに追加されていると思いEntityManager.merge()はテーブルレコードを作成しません

/*** 
* 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 transfer = new TransferRecord(); 
transfer.setTransferId(metaData.getTransferId()); 
transfer.setUserName(metaData.getUserId().getUserName()); 
transfer.setCancelled(false); 
em.merge(transfer); 
em.flush(); 

ここで転送レコードのコードです。私のコードは、TransferRecordレコードの有無をフラグとして使用します。したがって、データはすぐにテーブルに表示する必要があります。

私はおそらく私の前提になるでしょうか?もしそうなら、返す前にレコードを書き出すまで、em.flush()コールを強制的に呼び出す方法がありますか?

+0

あなたの声明の一部はかなり曖昧です。たとえば、「したがって、データはすぐにテーブルに表示する必要があります。」取引の観点からはほとんど意味をなさない。 'em.flush()'を実行すると、永続コンテキストがデータベースにフラッシュされます。異なるトランザクションにおけるこれらの変更の可視性は、トランザクション分離レベルの影響を受けます。したがって、あなたが望む行動を述べることは価値があるでしょう。また、 "em.flush()呼び出しが返す前にレコードを書き出すまで強制的に待機させる方法はありますか?"ブロッキングコールなので意味がありません。 –

+0

どのような変換が使用されていますか?「TransferRecordレコードの有無」のチェックはどこのコードが実行されますか? – ddewaele

+0

コードは別のBeanによって呼び出されるBeanで実行されます。呼び出し元のBeanはステートレスで、数秒間実行されます。 Beanの操作が終了するまで、データは他のデータベースユーザーに表示されません。 –

答えて

1

質問状態に付随するコメント -

「呼び出しBeanはステートレスであり、多くの秒の実行Beanの操作が終了するまで、データが他のデータベース・ユーザーには表示されません。」

この動作は、EntityManagerに関連付けられた永続コンテキストをフラッシュすることとは関係ありません。トランザクションに関連するトランザクション分離レベルとは大きく関係しています。各SLSBメソッドは、既定のトランザクション属性REQUIRED(これは既存のトランザクションを使用するか、または新しいトランザクションを使用する)と関連付けられている可能性があるため、ステートレスセッションBean(SLSB)に関連付けられたトランザクションは実際にはデータをデータベースにコミットします。 1);結果として生じる動作は、トランザクションが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 
    } 
} 

このアプローチは当然であり、取引の業務性質は、このようなアプローチの採用を可能にしている場合にのみお勧めします。通常、ビジネストランザクションの一部であるすべてのビジネスアクティビティを実際のトランザクションに分類する必要があります。

関連する問題