Challenge Engineer Life !

エンジニア人生を楽しみたい!仕事や趣味で学んだ技術的なことを書いていくブログです。

ディープコピー in JPA

2013/5/21追記
後々、このブログで書いたやり方はやめました(^^;
最終的には
過去に書いた「ディープコピー in JPA」の訂正
で書いた方式を採用しています。

プロト開発ではJPAを利用してデータベースを操作しています。
ORマッパーは初めて利用するので、まだ慣れない感じですが、勉強していて面白いです。

さて、開発の中であるトランザクションをまるっとコピーする処理が必要となりました。
具体的には商談-見積-製品…と親子関係のあるデータにおいて、商談をコピーして再利用する、といったようなものです。

ディープコピーの必要があるのですが、実装方法としては
・Clonableインタフェースを実装して自前でコピー処理を書く
Apache commons langライブラリを使ってSerializationUtils.clone()を使う
辺りがスタンダードのようです。
まずは試しにSerializationUtils.clone()を使った所、単純なオブジェクトは問題なくディープコピーできたのですが、Entityに対して行うと

Exception [TOPLINK-7242] (Oracle TopLink Essentials - 2006.7 (Build 060720)): oracle.toplink.essentials.exceptions.ValidationException
Exception Description: An attempt was made to traverse a relationship using indirection that had a null Session. This often occurs when an entity with an uninstantiated LAZY relationship is serialized and that lazy relationship is traversed after serialization. To avoid this issue, instantiate the LAZY relationship prior to serialization.

なんてエラーが出てしまいました。Entityの@OneToManyの部分でfetch=FetchType.EAGERはつけていない(LAZY)にしているのが悪いのでしょうか。

そんなこんなで色々調べていた所、Stackoverflowにこんな話もありました。
JPAでfindしたインスタンスのIDをnullにする
http://stackoverflow.com/questions/1106632/deep-copy-in-jpa
これが本当なら結構楽そうです。

試してみた所、うまく動きました。

OpportunityEntity copyOpportunity = oppEntityFacade.find(opportunity.getOpportunityId());
oppEntityFacade.detach(copyOpportunity);
copyOpportunity.setOpportunityId(null);
//以降、配下にあるコレクションの中でOpportunityIdをみるものもnull

DBのスキーマによっては微妙な場合もありそうですが、とりあえずプロトはこんな感じで実装してみました。

にほんブログ村 IT技術ブログへ
にほんブログ村 にほんブログ村 IT技術ブログ Javaへ
にほんブログ村