8

データベースでかなり複雑なオブジェクト グラフを扱っています。XStream を使用して、正常に動作するこのオブジェクト グラフをシリアル化および逆シリアル化しています。データベースに存在するオブジェクトのオブジェクト グラフをインポートすると、最初は一時的なものです。これは、ID がなく、休止状態がそれを認識していないためです。次に、新しく一時的にインポートされたオブジェクトのどのオブジェクトが既存の永続オブジェクトにマップされるかを判断することで、オブジェクト グラフの一部に ID を設定するビジネス ロジックを作成します。次に、Hibernate の merge() と saveOrUpdate() を使用します。

私が何をしているのかをよりよく理解するための擬似コード:

ComplexObject transObj = xstream.import("object.xml");
ComplexObject persistObj = someService.getObjByName(transObj.getName());
for (OtherObject o : c.getObjects()) {
    if (persistObj.getObjects().contains(o.getName())) {
        o.setId(persistObj.getObjectByName(o.getName()).getId())
    }
    ... set a bunch of other IDs deeper in the object graph ...
}

transObj = session.merge(transObj);
session.saveOrUpdate(transObj);

次のようなエラーが発生するため、これは機能しません。

   org.springframework.dao.InvalidDataAccessApiUsageException: deleted object would be re-saved by cascade (remove deleted object from associations): [com.......SomeObject#353296]; nested exception is org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): [com.......SomeObject#353296]

休止状態のマージは、一時的なオブジェクトを永続的なオブジェクトに関連付けるためのものではないようです。

セッションで永続オブジェクトを取得せずにやりたいことを達成する方法はありますか?一時オブジェクトを変更し、それを保存して既存の永続オブジェクトをオーバーライドするのではなく、それを変更しますか?

4

2 に答える 2

8

Hibernateマージは、一時オブジェクトを永続オブジェクトに関連付けるためのものではなかったようです

マージはJPA標準です-「新規およびデタッチされたエンティティインスタンス」を「(永続コンテキスト-)管理対象エンティティインスタンス」にマージします。Cascade.MERGEまたはCascade.ALLとマークされたFK関係間でカスケードされます。

JPAの観点から-いいえ、マージはXstreamストリーミングされた一時オブジェクトを永続に関連付けることを目的としていませんでした。JPAは、マージが通常のJPAライフサイクルで機能することを意図しています-新しいオブジェクトの作成、永続化、取得/検索、デタッチ、変更(新しいオブジェクトの追加を含む)、マージ、オプションでさらに変更してから、永続化/保存。これは意図的な設計であるため、JPAは合理化され、パフォーマンスが向上します。データベースに永続化する個々のオブジェクトの前に、挿入/更新するかどうか/何を挿入するかを決定するためにオブジェクトの状態を取得する必要はありません。JPA永続コンテキストオブジェクトの状態には、これを判断するのに十分な詳細がすでに含まれています。

問題は、デタッチされたかのように動作させたい新しいエンティティインスタンスがあることです。これはJPAの方法ではありません。

SaveOrUpdateは、休止状態のプロプライエタリ操作です。エンティティにIDがある場合は更新されますが、エンティティがない場合は挿入されます。

休止状態の観点から-はい、マージとそれに続くsaveOrUpdateは、理論的には(Xstreamストリーミングされた)一時オブジェクトを永続オブジェクトに関連付けるために機能しますが、JPA操作と組み合わせて使用​​すると制限が生じる場合があります。saveOrUpdateは、データベースに永続化する各オブジェクトの前に、挿入/更新するかどうか/何を挿入/更新するかを決定するための取得を行います。これはスマートですが、JPAではなく、最もパフォーマンスが高くありません。つまり、これを注意深く適切な構成で機能させることができるはずです。競合が発生した場合は、JPA操作ではなく休止状態の操作を使用します。

次のようなエラーが発生します。

org.hibernate.ObjectDeletedException:削除されたオブジェクトはカスケードによって再保存されます(削除されたオブジェクトを関連付けから削除します):[com ....... SomeObject#353296]

これは2つの要因が原因である可能性があると思います。

  • (Xstreamで作成された)オブジェクトグラフのどこかに、JPAを使用して(カスケード)取得を行った場合に存在する子エンティティインスタンスがありません。これにより、オブジェクトが削除されます。
  • (Xstreamで作成された)オブジェクトグラフの別の場所に、この同じ子エンティティが存在します。これにより、オブジェクトが再保存されます。

    これをデバッグする方法:(a)Xstreamからオブジェクトグラフを作成し、それを完全に印刷します-すべてのエンティティ、すべてのフィールド。(b)JPAを介して同じオブジェクトグラフをロードし、最上位のエンティティから取得をカスケードして完全に出力します-すべてのエンティティ、すべてのフィールド(c)2つを比較します-(a)に存在するものが(b)にありません)??

一時的なオブジェクトを変更し、それを保存して既存の永続的なオブジェクトをオーバーライドしようとする代わりに、セッションで永続オブジェクトを取得して変更することなく、やりたいことを達成する方法はありますか?

提案されたデバッグ/修正を介して-うまくいけば。

または、このバグを(ばかげて)回避するという私のブルートフォースの提案(ただし、パフォーマンスは少し低下します):オブジェクトグラフにIDを入力した後、EntityManager.clear()を呼び出し、merge()とsaveOrUpdate()に進みます。 。ただし、DBの結果を単体テストして、Xstreamグラフにデータを入力する際に​​重要なことを見逃していないことを確認してください。

最後の手段として/をテストするには、merge()なしでsaveOrUpdate()を試してください。ただし、NonUniqueObjectExceptionなどのHibernate例外を回避するために、エンティティマネージャーをclear()する必要がある場合があります。

あなたがもっと学ぶ/もっと情報を持っているなら私に知らせてくださいB^)

于 2013-03-18T05:09:54.590 に答える
1
    Hibernate merge was not meant for associating transient objects 
to persistent ones.

分離天体を実際に扱っているので、merge()は理想的には機能するはずです。マージを呼び出す前に「transObj」にIDを設定しているため、hibernateはそれらをデタッチされていると見なします(一時的ではありません)。

あなたのコードの問題は、それが休止状態でトリップしていることだと思います。

コードでは、データベースから「persistObj」をロードしています。現在、休止状態はこの「persistObj」をセッションで保持します。次に、「transObj」の「persistObj」からいくつかのIDを設定してから、mergeを呼び出します。'persistObj'と'transObj'の子オブジェクトの一部は同じIDを持っているため、休止状態は混乱します。

ComplexObject transObj = xstream.import("object.xml");
ComplexObject persistObj = someService.getObjByName(transObj.getName());
for (OtherObject o : c.getObjects()) {
    if (persistObj.getObjects().contains(o.getName())) {
        o.setId(persistObj.getObjectByName(o.getName()).getId())
     }
    ... set a bunch of other IDs deeper in the object graph ...
}
transObj = session.merge(transObj);
session.saveOrUpdate(transObj);

'persistObj'をロードした後にsession.clear()を呼び出してみてください。これにより、hibernateはpersistObjを削除し、分離されたオブジェクトのみを考慮します。

ComplexObject transObj = xstream.import("object.xml");
ComplexObject persistObj = someService.getObjByName(transObj.getName());
// Clear the session, so that hibernate removes 'persistObj' from it's cache
session.clear();
for (OtherObject o : c.getObjects()) {
    if (persistObj.getObjects().contains(o.getName())) {
        o.setId(persistObj.getObjectByName(o.getName()).getId())
     }
    ... set a bunch of other IDs deeper in the object graph ...
}
transObj = session.merge(transObj);
session.saveOrUpdate(transObj);
于 2013-03-18T18:06:50.177 に答える