Edited at

Entityのライフサイクルでハマりかけた話


はじめに


  • 今回は、Jpaを使う際に絶対に意識する必要があるEntityのライフサイクルについてとJpaの挙動について痛い目にあったので、出来事ベースでエントリを書かせてもらいます

  • そんなん知ってるよって方は僕の認識に齟齬があったら指摘ください


背景


  • Spring-data-Jpaを使ってから1年くらい経過しました

  • なんとなくメソッドの命名規則さえ意識すればクエリを自動生成してくれるんで便利だなと思っていました


きっかけ


  • 年始に入ってから、JSUG勉強会1回目でカサレアルの多田さんがSpring-data-Jpaを積極的に使う理由は見当たらないという旨の発言をされていまして、自分の中ではいまいち理解しきれずにいました(反抗的ですみませんmm)

  • それでも使いたいなら、パーフェクトJavaEEを10周してくださいとのことだったので、3~4周はしておこうと思い読んでおきました


JavaEEを読んだ感想


  • なんとなくで実装していた部分(例えばimplements seriarizableのとことか)知識がなかったので新たな発見は多かったです

  • ただ、Entityのライフサイクルはメソッドの自動実装に依存しまくってたので、意識することがないのかなと思ってました


そして、この伏線たちが・・・


  • ここまで伏線を張るともうお分かりかと思いますが、襲いかかってきましたwww


事の発端


  • とあるテーブルに対して主キー指定でかつ、日付を1週間の範囲指定で削除する実装をしていました

  • こんなかんじです


UserRepository.kt

interface UserRepository : JpaRepository<UserEntity, Int>{

fun deleteByIdAndBasedateBetweenAnd(startId: Int,start: Date,end :Date)
}



  • こいつを7万レコードくらい落としにいったときに100秒くらいかかりまして白目になりましたwww


原因


  • ぱっとみ原因はわからなかったので、ymlで一旦show-sql=trueにしました

  • すると、こんなかんじでしたwwww


sample.sql

select * from user where id =XX and basedate....

delete from user where id = XX and basedate=XX
delete from user where id = XX and basedate=XX
delete from user where id = XX and basedate=XX



  • ご覧の通り、全権取得して範囲内で1日ずつ指定して削除を実行していました

  • なので、一旦はメソッドに@QueryでJPQLで実装したことで実行時間は解決したのですが、もう1個疑問が生まれました


なんでselect走ってるんだろう?


  • 実装のイメージには省略しちゃいましたが、実はこのメソッドには@Transactionalがついています

  • このアノテーションを外すと動かなくなってしまうのですが、その原因はselectしてるところに関係がありました(ようするにEntityのライフサイクルが登場します)


Entityのライフサイクルとは


  • 基本的にはパーフェクトJavaEE読んでもらった方がわかりやすいので、簡単にしか説明しませんが、Entityの状態は大きく分けて4つあります

名前
説明

persist()
単にエンティティのインスタンスをnewしただけの状態。永続コンテキスト中で管理されていないため、状態を変更してもDBには一切反映されない。

find()
永続コンテキスト中で状態が管理された状態。適切なタイミングでDML文の犯行によりDBと同期がとられる。

remove()
永続コンテキスト中で削除が予約された状態。

merge()
一旦Managedな状態にあったエンティティが永続コンテキストから切り離された状態。


  • deleteはこの状態でいうremoveのことなのですが、永続コンテキスト中で状態が管理された状態に対してのみ実行されるので、selectを実行されてかつ同一のトランザクション内でないと実行してくれないということです

  • ここらへんの話を詳しく知りたい方は、まずは「jpa entity ライフサイクル」で画像検索してみてください


まとめ


  • Spring-data-Jpaをなんとなくで使うのはやっぱり危険

  • 具体的には、自分の思っている挙動と自動実装で生成されるクエリが一致するとは限らない

  • また、ライフサイクルを知らなくても使えることは使えるけど、ハマった時にまじで沼に入りかねない

  • なので当面はMybatis推しで行きたい

  • カサレアルの多田さんのおかげで命拾いしました。ありがとうございます。


参考

Java ORマッパー選定のポイント

パーフェクトJavaEE