SpringDataJPAと組み合わせてJPAのオブジェクト@Idとしてプリミティブ型を使用する際の問題を発見しました。親側のCascade.ALLと親子関係があり、子はPKを持ち、同時に親のFKでもあります。
class Parent {
@Id
private long id;
@OneToOne(mappedBy = "parent", cascade = ALL)
private Child child;
}
class Child {
@Id
@OneToOne
private Parent parent;
}
だから、私が実行すると:
...
Parent parent = new Parent();
Child child = new Child(parent);
parent.setChild(child);
em.persist(parent)
...
すべてが正常に動作します。ただし、Spring Data JPAを使用してエンティティを永続化したため、代わりに次のコマンドを実行します。
parentRepository.save(parent); // instead of em.persist(parent);
これは、次の例外を除いて失敗しました。
Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Parent
問題は、Spring Data JPA save()メソッドがエンティティが新しいかどうかをチェックし、新しい場合はem.persist()が使用され、そうでない場合はem.merge()が使用されることでした。
ここで興味深いのは、Springがエンティティが新しいかどうかをチェックする方法です。
getId(entity) == null;
そしてもちろん、これは誤りでした。@ Idの型としてlongを使用し、longのデフォルト値は0でした。longをLongに変更すると、すべてがSpringDataJPAでも機能します。
したがって、プリミティブ型ではなく、プリミティブ型(longではなくLongなど)のオブジェクトラッパーを常に使用することをお勧めします。これを推奨される方法として説明しているサードパーティのリソースは非常に便利です。