Cover Image for EclipseLinkでDBに存在しないレコードに対しての一意制約例外が発生する

EclipseLinkでDBに存在しないレコードに対しての一意制約例外が発生する

この記事は最終更新日から7年以上が経過しています。

TL;DR

@Id が付いているパラメタにDB上で auto_increment が設定されている場合、
そのパラメタにコード上で値を入れて persist() すると一意制約例外が発生する。

回避するためには persist() の代わりに merge() を使う。

解説

発生状況

persistence.xml に以下が設定されている

<property name="eclipselink.validate-existence" value="true"/>

コード上で @Entity のオブジェクトを new した後、全てのパラメタに値を詰めて persist() したところ

javax.persistence.EntityExistsException: Exception Description: Cannot persist detached object

が発生した。

DB上では、該当オブジェクトの @Id が付いているパラメタに auto_increment が設定されていた。

原因

http://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/p_validate_existence.htm
を見ると persistence context に存在しない場合にエラーが発生すると書いてあることから調査を開始してみた。

JPAで処理される際に、 auto_increment のカラムに @Id が付与されている場合
GeneratedValue strategy=GenerationType.SEQUENCE と判断される (@Id指定だけの時はDBみて自動判断? http://otndnld.oracle.co.jp/products/ias/toplink/jpa/resources/toplink-jpa-annotations.html#Id )

オブジェクトを persist() した際に
GeneratedValue strategy=GenerationType.SEQUENCE なのに @Id のパラメタに値が入っている
かつ PersistenceContext に入ってない → 分離状態(detached object)と判定
detached object なのに persist しようとしている
javax.persistence.EntityExistsException: Exception Description: Cannot persist detached object

という流れでエラーになっている

対処方法

  • @Id 指定されているパラメタに値が入っている場合は、persist() の代わりに merge() を使う
    • 参考 → http://stackoverflow.com/questions/10463460/cannot-persist-detached-object

参考

  • http://kokuzawa.github.io/blog/2014/01/31/entityexistsexception/
  • http://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/p_validate_existence.htm
  • http://stackoverflow.com/questions/10463460/cannot-persist-detached-object
  • http://otndnld.oracle.co.jp/products/ias/toplink/jpa/resources/toplink-jpa-annotations.html#Id
  • http://otndnld.oracle.co.jp/products/ias/toplink/jpa/resources/toplink-jpa-annotations.html#GeneratedValue
  • http://etc9.hatenablog.com/entry/20090830/1251606823