一意だが主キーではないキーに基づいて Hibernate オブジェクトを返すメソッドを作成しようとしています。エンティティがデータベースに既に存在する場合はそれを返したいのですが、そうでない場合は、新しいインスタンスを作成して保存してから返したいと思います。
更新:これを書いているアプリケーションは、基本的に入力ファイルのバッチプロセッサであることを明確にしましょう。システムは、ファイルを 1 行ずつ読み取り、データベースにレコードを挿入する必要があります。ファイル形式は基本的に、スキーマ内の複数のテーブルの非正規化ビューであるため、親レコードを解析してデータベースに挿入し、新しい合成キーを取得するか、既に存在する場合はそれを選択する必要があります。次に、そのレコードに戻る外部キーを持つ他のテーブルに関連するレコードを追加できます。
これが難しい理由は、各ファイルを完全にインポートするか、まったくインポートしない必要があるためです。つまり、特定のファイルに対して行われるすべての挿入と更新は、1 つのトランザクションの一部である必要があります。すべてのインポートを実行するプロセスが 1 つしかない場合、これは簡単ですが、可能であれば、これを複数のサーバーに分割したいと考えています。これらの制約のため、1 つのトランザクション内にとどまることができる必要がありますが、レコードが既に存在する例外を処理する必要があります。
親レコードのマップされたクラスは次のようになります。
@Entity
public class Foo {
@Id
@GeneratedValue(strategy = IDENTITY)
private int id;
@Column(unique = true)
private String name;
...
}
このメソッドを書く最初の試みは次のとおりです。
public Foo findOrCreate(String name) {
Foo foo = new Foo();
foo.setName(name);
try {
session.save(foo)
} catch(ConstraintViolationException e) {
foo = session.createCriteria(Foo.class).add(eq("name", name)).uniqueResult();
}
return foo;
}
問題は、探している名前が存在する場合、uniqueResult() の呼び出しによって org.hibernate.AssertionFailure 例外がスローされることです。完全なスタック トレースは以下のとおりです。
org.hibernate.AssertionFailure: null id in com.searchdex.linktracer.domain.LinkingPage entry (don't flush the Session after an exception occurs)
at org.hibernate.event.def.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:82) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:190) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:147) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:219) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:58) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1185) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1709) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:347) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.CriteriaImpl.uniqueResult(CriteriaImpl.java:369) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
この例外がスローされる原因を知っている人はいますか? 休止状態はこれを達成するためのより良い方法をサポートしていますか?
また、最初に挿入してから、それが失敗したかどうか、いつ失敗したかを選択する理由を先制的に説明しましょう。これは分散環境で機能する必要があるため、チェック全体で同期して、レコードが既に存在するかどうかと挿入を確認できません。これを行う最も簡単な方法は、すべての挿入で制約違反をチェックして、データベースにこの同期を処理させることです。