私は EclipseLink (2.3.0 と 2.4.1 の両方) でかなり奇妙な問題を発見しました。誰かがこれがバグであり、明らかな何かが欠けているだけではないことを確認できるかどうかを尋ねたかっただけです...
基本的に、2 つのエンティティが関係しています。単純に A と B と呼びましょう。A には、結合列を使用してデータベースでモデル化された B への熱心な (それが重要な場合) 非カスケード多対 1 の一方向参照がありました。DB テーブル A には列 ID (PK)、B_ID (FK から B.ID) などが含まれ、テーブル B には列 ID といくつかの列が含まれています。
これで、新しい B インスタンスへの参照で更新する必要がある As のリストができました。疑似コードでは次のようになります。
for(A a : list) {
// using some criteria, check if perhaps a matching B
// entity is already found in the database
B b = perhapsGetExistingBFromDatabase();
if(b == null) {
// no existing B was found, create a new
b = new B();
// set values in B
b.setFoo(4711),
b.setBar(42);
// save B
em.merge(b);
}
// set reference from A to B ...
a.setB(b);
// ... and update A
em.merge(a);
}
参照は非カスケードであったため、b と a の両方をマージする必要がありました。すべてが期待どおりに機能しました。
次に、誰か (私) がリレーションシップのカスケード タイプを none から merge/persist に変更しました。コードの別の場所で必要だったからです。古いコードが機能することを期待していましたが、b のマージは実際には必要ありません。簡単なテストで、それがまだ機能していることが確認され、新しい B エンティティが挿入され、それに応じて A が更新されました。
ただし、リストに A エンティティが 1 つしかない場合にのみ機能します。perhapsGetExistingBFromDatabase
ループを2回実行すると、「SELECT .. FROM B」が実行され、マージされたBエンティティがキャッシュされ、データベース表を最新の状態にする必要があるため、EclipseLinkによってセッションが自動フラッシュされます。コードで FINEST ロギング レベルとブレークポイントを使用して、EclipseLink が新しい B エンティティの ID を生成する必要があると判断し、シーケンス ジェネレータを呼び出し、ID をエンティティの正しいフィールドに設定することを確認できます。それでも、EclipseLink は次のような SQL ステートメントを呼び出します。
INSERT INTO B (ID, ...) VALUES(0, ...);
UPDATE A SET B_ID = 0 WHERE ID = ...;
生成された ID がどこかで失われ、EclipseLink は ID=0 で新しいエンティティを作成しようとします。データは確かに無効であり、後で、EclipseLink も PersistenceException をスローします。作業単位のクローンで主キーが Null またはゼロです。
バグか私のミスか?
編集: ジェームズは B.ID のマッピングを求めました:
@Id
@GeneratedValue(generator = "sq_idgen", strategy = GenerationType.SEQUENCE)
@SequenceGenerator(name = "sq_idgen", sequenceName = "...", allocationSize = 100)
@Column(name = "id")
protected long id;
em.merge(b);
不要なものを削除すると問題が解決することに注意してください。マージの呼び出しによって EclipseLink が完全に失敗し、ID が入力されていない B インスタンスを挿入しようとする理由は、私には明らかではありません。