はまる!!jpa #glassfish_jp #javaee
TRANSCRIPT
はまる! J PA
@ M A K I N G 槙 俊明
2 0 1 3 - 1 2 - 1 3 G L A S S F I S H U S E R S G R O U P J A PA N 勉強会 2 0 1 3 # 2
[宣伝 ] はじめてのB O O T S T R A P出版 !
• #はじboo
• http://bit.ly/hajiboo
• プレゼント用に1冊持ってきました!
みなさん J PAつかっていますか?
私の J PA経験
• とある企業のバックエンドシステム
• 約 50エンティティ、約 80 k LOC
• X億ユーザー・・・
• おれおれBlogシステム
• https://github.com/making/categolj2-backend
• 約 10エンティティ、now developing
僕が J PAを使って遭遇した
はまりポイントをだらだら述べていきます
動作確認環境
• GlassFish: 4
• JPA API: 2.1
• JPA Provider: Eclipse Link 2.5.0
• RDBMS: MySQL 5.1 (古い・・)
動作確認環境
• GlassFish: 4
• JPA API: 2.1
• JPA Provider: Eclipse Link 2.5.0
• RDBMS: MySQL 5.1 (古い・・)
はまったときは J PA 2 . 0
+ H I B E R N AT E 4
よくある注文履歴画面
O R D E R O R D E R _ I T E M I T E M
1…N M…1
E N T I T Yクラス
E N T I T Yクラス
N:1
E N T I T Yクラス
N:1
M : 1
一覧表示処理 E J B
FA C E L E T S
データ投入
• 注文件数: 100件
• 注文1件あたり5品注文
実行!
・・・
・・・遅い
間題に気づいた人いますか?
ログを確認
persistence.xmlに以下のプロパティを設定する
ログを確認…
ログを確認…
O R D E Rを検索して
ログを確認…
O R D E Rを検索して
O R D E R毎に O R D E R _ I T E Mを検索して
ログを確認…
O R D E Rを検索して
O R D E R毎に O R D E R _ I T E Mを検索して
O R D E R _ I T E M毎に I T E Mを検索
ログを確認…
O R D E Rを検索して
O R D E R毎に O R D E R _ I T E Mを検索して
O R D E R _ I T E M毎に I T E Mを検索
1 + 1 0 0 X ( 1 + 5 ) = 6 0 1回 S Q Lが実行されている・・・
はまりポイント 1 :
N + 1問題
F E T C H _ T Y P E = L A Z Yなので この段階では関連E N T I T YはF E T C Hされていない
F E T C H _ T Y P E = L A Z Yなので この段階では関連E N T I T YはF E T C Hされていない
関連E N T I T YにアクセスしたタイミングでS Q L発行
F E T C H _ T Y P E = L A Z Yなので この段階では関連E N T I T YはF E T C Hされていない
関連E N T I T YにアクセスしたタイミングでS Q L発行
関連E N T I T YにアクセスしたタイミングでS Q L発行
E J Bを修正
E J Bを修正
J O I N F E T C Hで 予めまとめてF E T C H
再実行!
速く・・・
速く・・・ない
ログを確認…
ログを確認…
O R D E RとO R D E R _ I T E Mは まとめて取得できているが
ログを確認…
O R D E RとO R D E R _ I T E Mは まとめて取得できているが
O R D E R _ I T E M毎に I T E Mを検索・・・
ログを確認…
O R D E RとO R D E R _ I T E Mは まとめて取得できているが
O R D E R _ I T E M毎に I T E Mを検索・・・1 + 5 0 0 = 5 0 1回
S Q Lが実行されている・・・
E J Bを確認
E J Bを確認
ここが効いていない
J PA仕様では・・・
• ネストしたJOIN FETCHはサポートされていない
• JOIN FETCHのエイリアスを設定できない
J PA仕様では・・・
• ネストしたJOIN FETCHはサポートされていない
• JOIN FETCHのエイリアスを設定できない
H I B E R N AT Eではサポートされている
E C L I P S E L I N Kでの対応方法
ヒントを追加
再実行!
速く・・・
速く・・・なった!
ログを確認
ログを確認
1回にまとまった!
教科書レベルのはまりポイントでした
補足
• 別解:NEW式を使う
• http://d.hatena.ne.jp/megascus/20120925/1348575449 参考
• EclipseLinkではデフォルトでキャッシュが効くため、2回目以降は速い・・・
• pesistence.xmlに以下を設定してキャッシュを無効にして検証
次いきましょう
とあるA C C O U N T更新画面
とあるA C C O U N T更新画面
A C C O U N T全件表示
とあるA C C O U N T更新画面
A C C O U N T全件表示
ボタンを押すと E M A I Lを更新
A C C O U T更新ロジック
• 入力されたEmailが既に別のユーザーが使用していた場合は例外スロー
• それ以外の場合はAccountを更新
更新してみる
更新してみる
使用されていないE M A I L
更新してみる
使用されていないE M A I L
更新してみる
使用されていないE M A I L
更新してみる
使用されていないE M A I L
ファッ!?
E J Bの実装を確認
間違いに気づいた人いますか?
ログを確認
ログを確認
ログを確認C O U N Tの前に
U P D AT Eが発行されている!?
はまりポイント 2 :
更新されちゃってた問題
E J Bの実装を確認
E J Bの実装を確認
E J Bの実装を確認F I N Dで取得したA C C O U N Tは
E N T I T Y M A N A G E R管理下
E J Bの実装を確認F I N Dで取得したA C C O U N Tは
E N T I T Y M A N A G E R管理下
E J Bの実装を確認F I N Dで取得したA C C O U N Tは
E N T I T Y M A N A G E R管理下
E N T I T Y M A N A G E R管理下 のエンティティを更新
E J Bの実装を確認F I N Dで取得したA C C O U N Tは
E N T I T Y M A N A G E R管理下
E N T I T Y M A N A G E R管理下 のエンティティを更新
E J Bの実装を確認F I N Dで取得したA C C O U N Tは
E N T I T Y M A N A G E R管理下
E N T I T Y M A N A G E R管理下 のエンティティを更新
クエリを実行する前に いったんエンティティを
D Bに反映させる
E J Bの実装を確認F I N Dで取得したA C C O U N Tは
E N T I T Y M A N A G E R管理下
E N T I T Y M A N A G E R管理下 のエンティティを更新
クエリを実行する前に いったんエンティティを
D Bに反映させる
E J Bの実装を確認F I N Dで取得したA C C O U N Tは
E N T I T Y M A N A G E R管理下
E N T I T Y M A N A G E R管理下 のエンティティを更新
クエリを実行する前に いったんエンティティを
D Bに反映させる
C O U N T = 1 になる
E J Bの実装を確認F I N Dで取得したA C C O U N Tは
E N T I T Y M A N A G E R管理下
E N T I T Y M A N A G E R管理下 のエンティティを更新
クエリを実行する前に いったんエンティティを
D Bに反映させる
C O U N T = 1 になる
E J Bの実装を確認F I N Dで取得したA C C O U N Tは
E N T I T Y M A N A G E R管理下
E N T I T Y M A N A G E R管理下 のエンティティを更新
クエリを実行する前に いったんエンティティを
D Bに反映させる
C O U N T = 1 になる
M E R G EはE N T I T Y M A N A G E R管理下から はずれたエンティティを管理下に戻す処理
E J Bの実装を確認F I N Dで取得したA C C O U N Tは
E N T I T Y M A N A G E R管理下
E N T I T Y M A N A G E R管理下 のエンティティを更新
クエリを実行する前に いったんエンティティを
D Bに反映させる
C O U N T = 1 になる
M E R G EはE N T I T Y M A N A G E R管理下から はずれたエンティティを管理下に戻す処理ここでは不要
E N T I T Yのライフサイクル
���
��������
������� ��
�������
���� ���
���������������
���������������
��������
��� ��
����������
E N T I T Yのライフサイクル
���
��������
������� ��
�������
���� ���
���������������
���������������
��������
��� ��
����������
ここのタイミングに注意! F L U S H、 J P Q L、・・・
E J Bを修正
E J Bを修正
E J Bを修正
最後に更新
E J Bを修正
最後に更新
コミットのタイミング (メソッド正常終了時 )で エンティティをD Bに反映させる
思った通りの結果に
次いきましょう
1対多 + 複合キーなケース
E J B
E J B
C A S C A D E _ T Y P E = A L L なので関連E N T I T Yもまとめて登録できる
テストデータ登録
テストデータ登録
C A S C A D E = A L L なのでN U L Lでいいはず・・
エラーで起動しない・・・
エラーで起動しない・・・
間違いに気づいた人いますか?
(これはちょっと難しい )
どっちに
書き込めばいいか
わからない
はまりポイント 3 :
どこで親 I Dを設定すればいいかわからない問題
苦肉の策
苦肉の策
コメントアウトして 単方向の関連に・・・
再実行
再実行
S Q Lが発行されたが、A R T I C L E _ I Dが設定されない・・
E J Bを修正
E J Bを修正
TA G以外をいったん保存
E J Bを修正
TA G以外をいったん保存
F L U S Hして I Dを採番
E J Bを修正
TA G以外をいったん保存
F L U S Hして I Dを採番
採番した I Dを設定して保存
動いた・・・!
これでいいのか!?
これでいいのか!?
• CASCADE_TYPE=ALLなのに関連エンティティを1つずつ保存している
• EJB実装のアドホック感・・・
• 双方向の関連ではエラーになる
これでいいのか!?
• CASCADE_TYPE=ALLなのに関連エンティティを1つずつ保存している
• EJB実装のアドホック感・・・
• 双方向の関連ではエラーになる
根本解決を!
調べたらあった!
E N T I T Y修正
複合キーの対応フィールド名を指定する
E J B (再 )
E J B (再 )
C A S C A D E _ T Y P E = A L L なので関連E N T I T Yもまとめて登録できる
うまく動いた!
(実は )ここまでの話は・・・
もともとH I B E R N AT Eではまっていた問題
• 余計なものまで更新されちゃう問題
• 削除したつもりが削除されていなかった問題
• StackOverflow問題
もともとH I B E R N AT Eではまっていた問題
• 余計なものまで更新されちゃう問題
• 削除したつもりが削除されていなかった問題
• StackOverflow問題
E C L I P S E L I N K版で調査間に合わず!
次回
はまる! J PA PA R T 2
へ続く・・・
重要なこと
• JPAを使う場合は、必ずSQLログを出力しよう。
• Entityのライフサイクルを把握しておこう。
• 根本解決を心がけよう。結果オーライな対処を続けていると「JPAこわい」で終わる
次回に続く ( ? )