こんにちは。javaは嫌い。只野です。
javaでJPA使ってEntityに検索結果をぶち込んでいた時の話です。その処理ではDB検索してEntityクラスで取得したあとに、検索条件を変更して同じEntityクラスを使い、別の結果を取得するといった処理をしました。最初の結果とあとの結果で値の比較をしたかったのです。
だがしかし!
なぜかあとに検索した結果に初回の検索結果が詰まっている!
なんでよ〜。これだからjavaは嫌いだよ。自分の無知を物のせいにするルートを通過したあとに原因を探るのです。
原因はなんてことはありません。Entityクラスで内に指定したIdで永続化が開始されている場合。同一Entityクラスを利用して新たにDB検索してもIdが既に永続化されている場合は永続化情報が返却されます。
具体的に記載すると下記のようなEntityクラスがあったとします。商品情報を保存するためで、コード、日毎に数量を集計したりする際に利用したいとします。Entity内のIdは検索結果内で被らないようにしないとならないので、コードと日の組み合わせで一意としています。
@Entity public class Product { /** コード */ @Id private String code; /** 日 */ @Id private Long day; /** 数量 */ private Long quantity; }
上記クラスを利用して、当月、前月の商品の販売数を比較したいので、最初の検索で当月、次の検索で前月を指定してい取得したとします。そうすると下記のような結果が取得でき、前月、当月が比較できるはずだったのです。
当月
コード | 日 | 数量 |
---|---|---|
apple | 1 | 10 |
apple | 2 | 7 |
apple | 3 | 4 |
前月
コード | 日 | 数量 |
---|---|---|
apple | 1 | 5 |
apple | 2 | 3 |
apple | 3 | 0 |
だが現実は最初の検索結果で既にEntityクラスの永続化が開始されていたので、次の検索結果でIdが被っているものは永続化情報で値が設定されるのです。今回だとコード、日をId指定しているので、上記例だと前月取得時にはすべて当月情報が設定されます。DB検索結果は一度同一Idで永続化されていないか確認して、されていたら永続化されている情報を返却する当動きがJPAと永続化を利用した動きとなります。
使うときはよく調べて使わないと思はぬバグを生むとしみじみと実感します。トランザクションの発行状態や永続化状態でも変わる話ですが、永続化されるEntityクラスをトランザクション内で何度か利用する場合はIdの指定方法に気をつけて利用する範囲内で被らないようにしておけば間違いないでしょう。今回の利用したかった用途であれば下記のように日付をキーにすればよかったのです。
@Entity public class Product { /** コード */ @Id private String code; /** 日付 */ @Id private date date; /** 数量 */ private Long quantity; }
本日はこの辺で。