🐙

JPA(4)LifeCycleと内部の流れを理解してみましょう

2022/12/20に公開

初めまして、Junior Back-end Developer、SIMOKITAZAWAです。
色々と足りないところがあるかと思いますがよろしくお願い致します。

私がZENNを始めた理由は、

  1. 私が勉強したことをまとめたい
  2. 勉強の内容を共有して一緒に成長したい
  3. 私が間違って理解している部分について先輩たちに教えてもらいたい

このような理由で始めることになりました。
たくさんの教えをお願いします。

JPAのLife Cycle

JPAには4つのLifeCycelがあります。

  1. new/transient
  2. managed
  3. detached
  4. removed

new/transient

Item item = new Item();
item.setId(1L);
item.setName(Macbook);

この状態は、ただSettingだけしている状態です。JPAと何も関係がないのでtransientと言います。

managed

Item item = new Item();
item.setId(1L);
item.setName(Macbook);

em.persist(item);

この状態はPersistence Contextの中にItemがあり、このItemをJPA管理するするのでmanagedと言います。しかし、まだDBにitemがSaveされてない状態です。

上のイメージを見るとem.persist()の時にはqueryが作成してないですが、
TransactionがCommitになる時点でquery作成されます。

detached、removed

detachedはPersistence contextから分離状態のことで、removedはオブジェクトを削除した状態のことです。

em.persistすることとem.findを通じてpersistence contextによって管理される状態です。
saveもfindもpersistence contextの中にある1次キャッシュで管理されているからです。
逆にjpaが管理しない状態をdetach状態と言います。

findのSELECT Queryが見えますが、その後のUpdate queryはTransacionのCommit前にdetachしたので見えないことがUpdate queryが作成されてないことがわかります。

JPA内部を理解しましょう

Persistence ContextはApplicationとDBの間にあるものだと思ってください。
その間にあるため、JPAを使用する利点があります。

1. Entity照会時に1次キャッシュで照会されます。

(1). itemを照会します。
(2). Persistence contextの中に1次キャッシュにpk=1Lが存在する場合、Entityオブジェクトをreturnします。

(1). pk = 2Lのitemを照会します。
(2). Persistence contextの中に存在するか確認して存在しない場合はDBで照会します。
(3). その後、DBで照会したitmeを1次キャッシュにSaveします。
(4). 1次キャッシュに保存されたitemをreturnしてくれます。

「GoodItme」を保存します。保存するInsert queryは見えますが、selcet queryが見えないことが分かります。なぜなら、persistence contextに保存し、同じpk値をfindしたため、1次キャッシュからデータを持ってきたからです。

新しいTransactionの中で100Lアイテムをfindしてみます。それではselect queryは1番だけ出なければなりません。1つ目はdbから取得する作業をするのでqueryが出て、2つ目は取得したデータがpersistence contextに保存されるからです。
これらの利点はありますが、それほど大きな利点ではありません。その理由は、entity ManagerはTransactionが終わると削除されるため、最終的には1次キャッシュにあるデータも削除されるからです。

2. Transactional write-behind

JPAはTransactional write-behind機能を提供してくれます。
mabookとipadのデータを保存するとします。Persistence contextにある1次キャッシュとTransactional write-behindにINSERT SQLを積み上げ、データを保存します。iPadも同じように保存してくれます。その後、Transaction Commitが始まると、積み上げていたsqlがすべてDBに送信されます。

3. Dirty Checking

どんな方法でJPAはUpdate queryを作成するか確認しましょう。
JPA(3)に載せたUpdateイメージでqueryを確認してください。queryが作成されてないです。

Item macbook = new Item();
item.setId(1L);
item.setName("macbook);

em.persist(macbook);
tx.commit();
Item macbook = em.find(Itme.class, 1L);
macbook.setName("Macbook 16 pro");

tx.commit();

macbookをsaveした後にMacBookの名前を変えます。そうしたら、saveと同じようにpersisしなければならないと思いますが、JPAはデータの変化をわかって自動的に変化してくれます。これができた理由はsnapshotを先に撮ってSnapshotのEntityと1次キャッシュのEntityと比較して同じObjectだったら修正されたデータを反映してくれるからです。

jpaが管理するデータは、persistence contextに入った時点でsnapshotを撮ります。
そして、変更が検出されれば、比較後update queryを生成し、dbに反映させます。

flushとは

persistence contextのupdate内容をdbに反映するものです。
flushが発生するとDirty Checking、Transactional write-behindストレージに登録、Transactional write-behindにあるqueryをデータベースに転送が開始されます。
ここでflushをすると、一次キャッシュが消えずに維持されます。Transactional write-behindのSql、そしてUpdate sqlのようなものがdbに反映される過程です。

TransactionのCommit前にQueryが作成されることが見えます。

結論

JPA使う時には必ずTransactionのところで使わないと何もできないです。

Discussion