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の実装に基づいて設計された全体として機能していますか?もしそうなら、私は前述の問題にどのようにして最善の対応ができますか?
マージは実際にはバージョンフィールドをインクリメントしません。オブジェクトが呼び出し元にシリアル化されているときに、まだバージョンフィールドを変更します。メソッド内でem.flush()を呼び出すと、コンテキストが同期され、オブジェクトが返される前にフィールドがインクリメントされます。 – Chris
@Chrisそれは理にかなっています。私は間違った印象のもとで、バージョンフィールドがマージ中にインクリメントされるようになっていました。しかし、実際の動作は私には奇妙に思える。なぜなら、別のトランザクションが前述のマージ後に同じエンティティを読み込み、更新しようとするが、フラッシュ前に古いバージョンの値を受け取り、エンティティマネージャがフラッシュすると、その間。私の仮定は正しいのですか?もしそうなら、この意思決定の背後には論理的根拠がありますか? –
EntityManagerコンテキストはトランザクションを表しているため、他のコンテキスト/トランザクションとは完全に分離されています。トランザクションがコミットするまでは、バージョンやその他の変更は他のプロセスでは選択できません。 JPAは、ほとんどの例外は永続/マージ・コールのいずれかで発生するか、操作の性質に応じてコンテキストがデータベース(フラッシュ/コミット)と同期するまで遅延する可能性があると述べています。オプティミスティック・ロックは、これらのコリジョンが頻繁でなく、再試行がすべての操作のペシミスティック・ロックよりも安価であるシステムでのものです。 – Chris