2017-03-13 5 views
0

EclipseLink 2.5とコンテナ管理のトランザクションを使用するアプリケーションで、分離エンティティを時々マージする必要がある場合、各エンティティには@Versionアノテーションのあるフィールドが含まれます。エンティティがDTOにマップされてクライアントに送信されるため、エンティティが対応するDTOに加えられた変更に基づいてこれらのエンティティの更新を要求する可能性があるため、オプティミスティック・ロックの実装が必要です。私が直面している問題は、persist()またはmerge()がエンティティ上で呼び出されるたびに、persist()のケースで永続コンテキストに追加される対応するエンティティまたはmerge()によって返される更新されたエンティティには、更新されたバージョンフィールドが含まれないということです。@VersionのJPA/EclipseLink処理

@Stateless 
public class FooEjb { 

    @PersistenceContext(unitName = "FooApp") 
    private EntityManager entityManager; 


    public FooEntity create() { 

     FooEntity entity = new FooEntity(); 
     entity.setDescription("bar"); 

     entityManager.persist(entity); 

     return entity; 
    } 

    public FooEntity update(Long fooId, String description) { 
     FooEntity entityToUpdate = entityManager.find(FooEntity.class, fooId); 

     if (entityToUpdate != null) { 
      entityToUpdate.setDescription(description); 
      return entityManager.merge(entityToUpdate); 
     } 
     return null; 
    } 
} 

これらのEJBメソッドを呼び出す:このエンティティは、その後持続します

/以下と同様の方法でEJBに合併

@Entity 
public class FooEntity implements Serializable { 

    @Id 
    @GeneratedValue(generator = "MyGenerator") 
    private Long fooId; 

    @Version 
    private Long version; 

    @Column(nullable = false) 
    private String description; 

    // generic setters and getters 
} 
:例を介してこれを実証するために、我々は以下のエンティティがあるとし

FooEntity newEntity = fooEjbInstance.create(); 
newEntity.getFooId(); // returns the generated, non-null value; for the sake of example 43L 
newEntity.getVersion(); // returns null 
entityManager.find(FooEntity.class, 43L).getVersion(); // returns 1L 

// entity with fooId == 42L exists and has a version value of 1L 
FooEntity updatedEntity = fooEjbInstance.update(42L, "fhtagn"); 
updatedEntity.getVersion(); // returns the old value, i.e. 1L 
entityManager.find(FooEntity.class, 42L).getVersion(); // returns 2L 

これは、クライアントによって行われた任意の状態の変更を永続化することができないよう、クライアントに渡すのに戻ったエンティティが不適当デュ:以下の挙動を示していますeをmerge/persistコールに送信して、正しくOptimisticLockExceptionを引き起こします。

EJBメソッドには、明示的に注釈が付けられていない@TransactionAttributeがあります。JPA仕様では、デフォルト値TransactionAttributeType.REQUIREDが適用されるはずです。現在の理論は、ここで認識される現象は、トランザクションがコミットされたときにのみ更新されるバージョンフィールドと関係しているということです。上記のEJBメソッドのいずれかが返されるまでに、関連付けられたトランザクションはまだコミットされていません(の後にすぐにがコミットされます)、バージョンフィールドはまだ更新されていません。 this questionに記載されているバージョンのオブジェクトをキャッシュに格納しているのと対比して説明しましたが、私はこれに関する決定的な文書を見つけることができませんでした。これは、JPA 2.0またはEclipseLinkの実装に基づいて設計された全体として機能していますか?もしそうなら、私は前述の問題にどのようにして最善の対応ができますか?

+4

マージは実際にはバージョンフィールドをインクリメントしません。オブジェクトが呼び出し元にシリアル化されているときに、まだバージョンフィールドを変更します。メソッド内でem.flush()を呼び出すと、コンテキストが同期され、オブジェクトが返される前にフィールドがインクリメントされます。 – Chris

+0

@Chrisそれは理にかなっています。私は間違った印象のもとで、バージョンフィールドがマージ中にインクリメントされるようになっていました。しかし、実際の動作は私には奇妙に思える。なぜなら、別のトランザクションが前述のマージ後に同じエンティティを読み込み、更新しようとするが、フラッシュ前に古いバージョンの値を受け取り、エンティティマネージャがフラッシュすると、その間。私の仮定は正しいのですか?もしそうなら、この意思決定の背後には論理的根拠がありますか? –

+1

EntityManagerコンテキストはトランザクションを表しているため、他のコンテキスト/トランザクションとは完全に分離されています。トランザクションがコミットするまでは、バージョンやその他の変更は他のプロセスでは選択できません。 JPAは、ほとんどの例外は永続/マージ・コールのいずれかで発生するか、操作の性質に応じてコンテキストがデータベース(フラッシュ/コミット)と同期するまで遅延する可能性があると述べています。オプティミスティック・ロックは、これらのコリジョンが頻繁でなく、再試行がすべての操作のペシミスティック・ロックよりも安価であるシステムでのものです。 – Chris

答えて

1

マージと持続はすぐにデータベースに行く必要はないので、トリガー、シーケンス、バージョンに依存する操作では、flushまたはcommitを呼び出してそれらの値を設定する必要があります。 Flushは、コンテキストとデータベースとの同期を強制し、管理対象オブジェクトに適切に値を設定する必要があります。永続呼び出しでID生成を設定することができます。これは、順序が事前割り当てを可能にするが、IDオブジェクトまたはトリガーが使用されるときは通常起こりません。

EntityManagerコンテキストはトランザクションを表すため、他のコンテキスト/トランザクションとは完全に分離されています。トランザクションがコミットするまでは、バージョンやその他の変更は他のプロセスでは取得できないため、他のプロセスとの同期がいつ発生しても問題にはなりません。 JPAは、ほとんどの例外は永続/マージ・コールのいずれかで発生するか、操作の性質に応じてコンテキストがデータベース(フラッシュ/コミット)と同期するまで遅延する可能性があると述べています。オプティミスティックロックは、これらの衝突が頻繁でなく、再試行が各操作の悲観的なロックよりも安価であるシステムを対象としています。

関連する問題