これについて同様の投稿がたくさんあることは知っていますが、私の問題に対する明確な答えを見つけることができませんでした。
できるだけ簡単にするために、私がそのようなエンティティを持っているとしましょう:
@Entity
public class Person implements Serializable {
@Id
private Long id; // PK
private String name; // business key
/* getters and setters */
/*
override equals() and hashCode()
to use the **name** field
*/
}
つまり、id
PKでname
あり、ビジネスキーです。保存したい名前のリストが重複している可能性があるとしましょう。名前ごとに1つのオブジェクトを作成し、JPAに永続化させると、最終的なテーブルに重複する名前が含まれることになります。これは受け入れられません。
私の質問は、以下で説明する代替案と(特に歓迎する)あなた自身の代替案を考慮して、あなたが最善のアプローチだと思うものです。
考えられる解決策1:エンティティマネージャーを確認する
新しい人物オブジェクトを作成する前に、同じ人物名のオブジェクトがすでに管理されているかどうかを確認してください。 問題:エンティティマネージャーはPKによってのみ照会できます。わからない回避策はありますか?
考えられる解決策2:クエリでオブジェクトを見つける
Query query = em.createQuery("SELECT p FROM Person p WHERE p.name = ...");
List<Person> list = query.getResultList();
質問:要求されたオブジェクトはすでにemにロードされている必要がありますが、これは引き続きデータベースからフェッチされますか?もしそうなら、クエリを解析するために、非常に頻繁に実行された場合、それでもあまり効率的ではないと思いますか?
考えられる解決策3:別の辞書を保持する
これが可能なのは、equals()とhashCode()がフィールドを使用するためにオーバーライドされるためname
です。
Map<String,Person> personDict = new HashMap<String,Person>();
for(String n : incomingNames) {
Person p = personDict.get(n);
if (p == null) {
p = new Person();
p.setName(n);
em.persist(p);
personDict.put(n,p);
}
// do something with it
}
問題1:大規模なコレクションのメモリを浪費します。これは基本的にエンティティマネージャが行うことです(ただし、完全ではありません)。
問題2:より複雑なスキーマがあり、最初の書き込み後にアプリケーションが閉じられて再起動され、データベースを再ロードする必要があるとします。すべてのテーブルが明示的にemにロードされている場合、ディクショナリを簡単に再入力できます(エンティティごとに1つ)が、レイジーフェッチやカスケード読み取りを使用する場合は、それほど簡単ではありません。
私は最近JPA(EclipseLinkを使用)を開始しました。この問題は非常に一般的な使用パターンに要約されるように思われるため、ここで基本的なものが欠落している可能性があります。
教えてください!