一方向の多対 1 の関係を持つ 2 つのエンティティがあります。
- 多く
Bar
の人が持っていますFoo
マネージド インスタンスを既存のデタッチされたBar
インスタンスに変更して更新しようとすると、DB へのフラッシュが失敗します。Foo
Foo
ファサード ( FooFacade
& BarFacade
) を使用して、エンティティを作成および変更します。ここに私のテストコードがあります:
public void test() {
FooFacade fooFacade = (FooFacade) lookupEJB(FooFacade.class);
BarFacade barFacade = (BarFacade) lookupEJB(BarFacade.class);
Foo originalFoo = fooFacade.createFoo(); // returned entity is detatched
Foo newFoo = fooFacade.createFoo(); // returned entity is detatched
Bar bar = barFacade.createBar(originalFoo); // returned entity is detatched
barFacade.changeFoo(bar.getId(), newFoo);
}
このコードにより、次の SQL エラーが発生します。
'FOO' で定義された 'SQL130321134048610' で識別される一意または主キーの制約または一意のインデックスでキー値が重複する可能性があるため、ステートメントは中止されました。
すでに存在しているにもかかわらず、 Eclipselink はnewFoo
の変更をフラッシュするときに挿入しようとしbar
ています!
ただし、newFoo
インスタンスがデタッチされているという事実は違いはありません。以下の太字のEJB 永続化仕様(セクション 3.2.3、50 ページ)の関連部分を参照してください。
エンティティ X に適用されるフラッシュ操作のセマンティクスは次のとおりです。
- X が管理エンティティの場合、データベースに同期されます。
- X からの関係によって参照されるすべてのエンティティー Y について、Y への関係がカスケード要素値 cascade=PERSIST または cascade= ALL で注釈付けされている場合、持続操作が Y に適用されます。
- X からの関係によって参照されるエンティティー Y の場合、Y への関係にカスケード要素値 cascade=PERSIST または cascade= ALL の注釈が付けられていない場合:
- Y が新規または削除された場合、フラッシュ操作によって IllegalStateException がスローされる (およびトランザクションがロールバックされる) か、トランザクションのコミットが失敗します。
- Y が分離されている場合、セマンティクスは関係の所有権に依存します。X がリレーションシップを所有している場合、リレーションシップに対するすべての変更はデータベースと同期されます。それ以外の場合、Y が関係を所有している場合、動作は未定義です。
- X が削除されたエンティティである場合、データベースから削除されます。カスケード オプションは関係ありません。
これが機能しないように見える理由を説明できる人はいますか? 既存の eclipselink バグ レポートが見つかりません。以下にいくつかのコードがあり、テクノロジーは次のとおりです。
- EclipseLink (組み込み Glassfish EJB コンテナーを使用したテスト)
- Derby (この問題をテストするため - 本番環境では MS SQL を使用していますが、これも失敗します)
ありがとう!
注意してください:私は私がこれを機能させることができることを理解しています
Foo
更新時に使用されるのと同じ永続コンテキストに切り離されたものをマージしbar
、次に- のインスタンスをマージし
foo
た設定bar
。ただし、これは私のアプリケーションで採用したいアプローチではありません。
更新 1:これが機能することを示唆する JPA 仕様の一部を含めました
更新 2:サンプル シナリオを簡略化
エンティティ
@Entity
@TableGenerator(name="test_generator", table="SEQUENCE", pkColumnName="SEQ_NAME", valueColumnName="SEQ_VALUE", pkColumnValue="TEST_SEQUENCE")
public class Foo {
@Id
@GeneratedValue(strategy=GenerationType.TABLE, generator="test_generator")
private int id;
public int getId() {return this.id;}
public void setId(int id) {this.id = id;}
}
と
@Entity
public class Bar {
@Id
@GeneratedValue(strategy=GenerationType.TABLE, generator="test_generator")
private int id;
@ManyToOne
private Foo foo;
public int getId() {return this.id;}
public void setId(int id) {this.id = id;}
public Foo getFoo() {return this.foo;}
public void setFoo(Foo foo) {this.foo = foo;}
}
ファサード
@Singleton
@LocalBean
public class FooFacade {
@PersistenceContext(unitName="CurriculumManagementSystem")
private EntityManager em;
public Foo createFoo() {
Foo newFoo = new Foo();
em.persist(newFoo);
return newFoo;
}
}
と
@Singleton
@LocalBean
public class BarFacade {
@PersistenceContext(unitName="CurriculumManagementSystem")
private EntityManager em;
public Bar createBar(Foo parent) {
Bar newBar = new Bar();
newBar.setFoo(parent);
em.persist(newBar);
return newBar;
}
public void changeFoo(int barID, Foo detachedFoo) {
Bar bar = (Bar) em.find(Bar.class, barID);
bar.setFoo(detachedFoo);
// when this method exits, the transaction completes
// and any changes are flushed to the DB.
// instead of only updating the Bar table by changing the
// foreign key reference to the detachedFoo's ID, elipselink
// tries to insert the detachedFoo as a new row in the
// Foo table!
}