はまる!!jpa #glassfish_jp #javaee

120
はまる! JPA @MAKING 俊明 2013-12-13 GLASSFISH USERS GROUP JAPAN 勉強会 2013 #2

Upload: toshiaki-maki

Post on 10-May-2015

3.212 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: はまる!!JPA #glassfish_jp #javaee

はまる! 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

Page 2: はまる!!JPA #glassfish_jp #javaee

自己紹介

• @making

• 大手SIerでR&D

• JJUG幹事

• 金魚本(http://amzn.to/JavaEE6)を一部翻訳

Page 3: はまる!!JPA #glassfish_jp #javaee

[宣伝 ] はじめてのB O O T S T R A P出版 !

• #はじboo

• http://bit.ly/hajiboo

• プレゼント用に1冊持ってきました!

Page 4: はまる!!JPA #glassfish_jp #javaee

みなさん J PAつかっていますか?

Page 5: はまる!!JPA #glassfish_jp #javaee

私の J PA経験

• とある企業のバックエンドシステム

• 約 50エンティティ、約 80 k LOC

• X億ユーザー・・・

• おれおれBlogシステム

• https://github.com/making/categolj2-backend

• 約 10エンティティ、now developing

Page 6: はまる!!JPA #glassfish_jp #javaee

僕が J PAを使って遭遇した

はまりポイントをだらだら述べていきます

Page 7: はまる!!JPA #glassfish_jp #javaee

動作確認環境

• GlassFish: 4

• JPA API: 2.1

• JPA Provider: Eclipse Link 2.5.0

• RDBMS: MySQL 5.1 (古い・・)

Page 8: はまる!!JPA #glassfish_jp #javaee

動作確認環境

• 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

Page 9: はまる!!JPA #glassfish_jp #javaee

よくある注文履歴画面

O R D E R O R D E R _ I T E M I T E M

1…N M…1

Page 10: はまる!!JPA #glassfish_jp #javaee

E N T I T Yクラス

Page 11: はまる!!JPA #glassfish_jp #javaee

E N T I T Yクラス

N:1

Page 12: はまる!!JPA #glassfish_jp #javaee

E N T I T Yクラス

N:1

M : 1

Page 13: はまる!!JPA #glassfish_jp #javaee

一覧表示処理 E J B

FA C E L E T S

Page 14: はまる!!JPA #glassfish_jp #javaee

データ投入

• 注文件数: 100件

• 注文1件あたり5品注文

Page 15: はまる!!JPA #glassfish_jp #javaee

実行!

Page 16: はまる!!JPA #glassfish_jp #javaee

・・・

Page 17: はまる!!JPA #glassfish_jp #javaee

・・・遅い

Page 18: はまる!!JPA #glassfish_jp #javaee

間題に気づいた人いますか?

Page 19: はまる!!JPA #glassfish_jp #javaee

ログを確認

persistence.xmlに以下のプロパティを設定する

Page 20: はまる!!JPA #glassfish_jp #javaee

ログを確認…

Page 21: はまる!!JPA #glassfish_jp #javaee

ログを確認…

O R D E Rを検索して

Page 22: はまる!!JPA #glassfish_jp #javaee

ログを確認…

O R D E Rを検索して

O R D E R毎に O R D E R _ I T E Mを検索して

Page 23: はまる!!JPA #glassfish_jp #javaee

ログを確認…

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を検索

Page 24: はまる!!JPA #glassfish_jp #javaee

ログを確認…

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が実行されている・・・

Page 25: はまる!!JPA #glassfish_jp #javaee

はまりポイント 1 :

N + 1問題

Page 26: はまる!!JPA #glassfish_jp #javaee
Page 27: はまる!!JPA #glassfish_jp #javaee

F E T C H _ T Y P E = L A Z Yなので この段階では関連E N T I T YはF E T C Hされていない

Page 28: はまる!!JPA #glassfish_jp #javaee

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発行

Page 29: はまる!!JPA #glassfish_jp #javaee

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発行

Page 30: はまる!!JPA #glassfish_jp #javaee

E J Bを修正

Page 31: はまる!!JPA #glassfish_jp #javaee

E J Bを修正

J O I N F E T C Hで 予めまとめてF E T C H

Page 32: はまる!!JPA #glassfish_jp #javaee

再実行!

Page 33: はまる!!JPA #glassfish_jp #javaee

速く・・・

Page 34: はまる!!JPA #glassfish_jp #javaee

速く・・・ない

Page 35: はまる!!JPA #glassfish_jp #javaee

ログを確認…

Page 36: はまる!!JPA #glassfish_jp #javaee

ログを確認…

O R D E RとO R D E R _ I T E Mは まとめて取得できているが

Page 37: はまる!!JPA #glassfish_jp #javaee

ログを確認…

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を検索・・・

Page 38: はまる!!JPA #glassfish_jp #javaee

ログを確認…

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が実行されている・・・

Page 39: はまる!!JPA #glassfish_jp #javaee

E J Bを確認

Page 40: はまる!!JPA #glassfish_jp #javaee

E J Bを確認

ここが効いていない

Page 41: はまる!!JPA #glassfish_jp #javaee

J PA仕様では・・・

• ネストしたJOIN FETCHはサポートされていない

• JOIN FETCHのエイリアスを設定できない

Page 42: はまる!!JPA #glassfish_jp #javaee

J PA仕様では・・・

• ネストしたJOIN FETCHはサポートされていない

• JOIN FETCHのエイリアスを設定できない

H I B E R N AT Eではサポートされている

Page 43: はまる!!JPA #glassfish_jp #javaee

E C L I P S E L I N Kでの対応方法

ヒントを追加

Page 44: はまる!!JPA #glassfish_jp #javaee

再実行!

Page 45: はまる!!JPA #glassfish_jp #javaee

速く・・・

Page 46: はまる!!JPA #glassfish_jp #javaee

速く・・・なった!

Page 47: はまる!!JPA #glassfish_jp #javaee

ログを確認

Page 48: はまる!!JPA #glassfish_jp #javaee

ログを確認

1回にまとまった!

Page 49: はまる!!JPA #glassfish_jp #javaee

教科書レベルのはまりポイントでした

Page 50: はまる!!JPA #glassfish_jp #javaee

補足

• 別解:NEW式を使う

• http://d.hatena.ne.jp/megascus/20120925/1348575449 参考

• EclipseLinkではデフォルトでキャッシュが効くため、2回目以降は速い・・・

• pesistence.xmlに以下を設定してキャッシュを無効にして検証

Page 51: はまる!!JPA #glassfish_jp #javaee

次いきましょう

Page 52: はまる!!JPA #glassfish_jp #javaee

とあるA C C O U N T更新画面

Page 53: はまる!!JPA #glassfish_jp #javaee

とあるA C C O U N T更新画面

A C C O U N T全件表示

Page 54: はまる!!JPA #glassfish_jp #javaee

とあるA C C O U N T更新画面

A C C O U N T全件表示

ボタンを押すと E M A I Lを更新

Page 55: はまる!!JPA #glassfish_jp #javaee

A C C O U T更新ロジック

• 入力されたEmailが既に別のユーザーが使用していた場合は例外スロー

• それ以外の場合はAccountを更新

Page 56: はまる!!JPA #glassfish_jp #javaee

更新してみる

Page 57: はまる!!JPA #glassfish_jp #javaee

更新してみる

使用されていないE M A I L

Page 58: はまる!!JPA #glassfish_jp #javaee

更新してみる

使用されていないE M A I L

Page 59: はまる!!JPA #glassfish_jp #javaee

更新してみる

使用されていないE M A I L

Page 60: はまる!!JPA #glassfish_jp #javaee

更新してみる

使用されていないE M A I L

ファッ!?

Page 61: はまる!!JPA #glassfish_jp #javaee

E J Bの実装を確認

Page 62: はまる!!JPA #glassfish_jp #javaee

間違いに気づいた人いますか?

Page 63: はまる!!JPA #glassfish_jp #javaee

ログを確認

Page 64: はまる!!JPA #glassfish_jp #javaee

ログを確認

Page 65: はまる!!JPA #glassfish_jp #javaee

ログを確認C O U N Tの前に

U P D AT Eが発行されている!?

Page 66: はまる!!JPA #glassfish_jp #javaee

はまりポイント 2 :

更新されちゃってた問題

Page 67: はまる!!JPA #glassfish_jp #javaee

E J Bの実装を確認

Page 68: はまる!!JPA #glassfish_jp #javaee

E J Bの実装を確認

Page 69: はまる!!JPA #glassfish_jp #javaee

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管理下

Page 70: はまる!!JPA #glassfish_jp #javaee

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管理下

Page 71: はまる!!JPA #glassfish_jp #javaee

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管理下 のエンティティを更新

Page 72: はまる!!JPA #glassfish_jp #javaee

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管理下 のエンティティを更新

Page 73: はまる!!JPA #glassfish_jp #javaee

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に反映させる

Page 74: はまる!!JPA #glassfish_jp #javaee

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に反映させる

Page 75: はまる!!JPA #glassfish_jp #javaee

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 になる

Page 76: はまる!!JPA #glassfish_jp #javaee

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 になる

Page 77: はまる!!JPA #glassfish_jp #javaee

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管理下から はずれたエンティティを管理下に戻す処理

Page 78: はまる!!JPA #glassfish_jp #javaee

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管理下から はずれたエンティティを管理下に戻す処理ここでは不要

Page 79: はまる!!JPA #glassfish_jp #javaee

E N T I T Yのライフサイクル

���

��������

������� ��

�������

���� ���

���������������

���������������

��������

��� ��

����������

Page 80: はまる!!JPA #glassfish_jp #javaee

E N T I T Yのライフサイクル

���

��������

������� ��

�������

���� ���

���������������

���������������

��������

��� ��

����������

ここのタイミングに注意! F L U S H、 J P Q L、・・・

Page 81: はまる!!JPA #glassfish_jp #javaee

E J Bを修正

Page 82: はまる!!JPA #glassfish_jp #javaee

E J Bを修正

Page 83: はまる!!JPA #glassfish_jp #javaee

E J Bを修正

最後に更新

Page 84: はまる!!JPA #glassfish_jp #javaee

E J Bを修正

最後に更新

コミットのタイミング (メソッド正常終了時 )で エンティティをD Bに反映させる

Page 85: はまる!!JPA #glassfish_jp #javaee

思った通りの結果に

Page 86: はまる!!JPA #glassfish_jp #javaee

次いきましょう

Page 87: はまる!!JPA #glassfish_jp #javaee

1対多 + 複合キーなケース

Page 88: はまる!!JPA #glassfish_jp #javaee

E J B

Page 89: はまる!!JPA #glassfish_jp #javaee

E J B

C A S C A D E _ T Y P E = A L L なので関連E N T I T Yもまとめて登録できる

Page 90: はまる!!JPA #glassfish_jp #javaee

テストデータ登録

Page 91: はまる!!JPA #glassfish_jp #javaee

テストデータ登録

C A S C A D E = A L L なのでN U L Lでいいはず・・

Page 92: はまる!!JPA #glassfish_jp #javaee

エラーで起動しない・・・

Page 93: はまる!!JPA #glassfish_jp #javaee

エラーで起動しない・・・

Page 94: はまる!!JPA #glassfish_jp #javaee

間違いに気づいた人いますか?

(これはちょっと難しい )

Page 95: はまる!!JPA #glassfish_jp #javaee

どっちに

書き込めばいいか

わからない

Page 96: はまる!!JPA #glassfish_jp #javaee

はまりポイント 3 :

どこで親 I Dを設定すればいいかわからない問題

Page 97: はまる!!JPA #glassfish_jp #javaee

苦肉の策

Page 98: はまる!!JPA #glassfish_jp #javaee

苦肉の策

コメントアウトして 単方向の関連に・・・

Page 99: はまる!!JPA #glassfish_jp #javaee

再実行

Page 100: はまる!!JPA #glassfish_jp #javaee

再実行

S Q Lが発行されたが、A R T I C L E _ I Dが設定されない・・

Page 101: はまる!!JPA #glassfish_jp #javaee

E J Bを修正

Page 102: はまる!!JPA #glassfish_jp #javaee

E J Bを修正

TA G以外をいったん保存

Page 103: はまる!!JPA #glassfish_jp #javaee

E J Bを修正

TA G以外をいったん保存

F L U S Hして I Dを採番

Page 104: はまる!!JPA #glassfish_jp #javaee

E J Bを修正

TA G以外をいったん保存

F L U S Hして I Dを採番

採番した I Dを設定して保存

Page 105: はまる!!JPA #glassfish_jp #javaee

動いた・・・!

Page 106: はまる!!JPA #glassfish_jp #javaee

これでいいのか!?

Page 107: はまる!!JPA #glassfish_jp #javaee

これでいいのか!?

• CASCADE_TYPE=ALLなのに関連エンティティを1つずつ保存している

• EJB実装のアドホック感・・・

• 双方向の関連ではエラーになる

Page 108: はまる!!JPA #glassfish_jp #javaee

これでいいのか!?

• CASCADE_TYPE=ALLなのに関連エンティティを1つずつ保存している

• EJB実装のアドホック感・・・

• 双方向の関連ではエラーになる

根本解決を!

Page 109: はまる!!JPA #glassfish_jp #javaee

調べたらあった!

Page 110: はまる!!JPA #glassfish_jp #javaee

E N T I T Y修正

複合キーの対応フィールド名を指定する

Page 111: はまる!!JPA #glassfish_jp #javaee

E J B (再 )

Page 112: はまる!!JPA #glassfish_jp #javaee

E J B (再 )

C A S C A D E _ T Y P E = A L L なので関連E N T I T Yもまとめて登録できる

Page 113: はまる!!JPA #glassfish_jp #javaee

うまく動いた!

Page 114: はまる!!JPA #glassfish_jp #javaee

(実は )ここまでの話は・・・

Page 115: はまる!!JPA #glassfish_jp #javaee
Page 116: はまる!!JPA #glassfish_jp #javaee

もともとH I B E R N AT Eではまっていた問題

• 余計なものまで更新されちゃう問題

• 削除したつもりが削除されていなかった問題

• StackOverflow問題

Page 117: はまる!!JPA #glassfish_jp #javaee

もともとH I B E R N AT Eではまっていた問題

• 余計なものまで更新されちゃう問題

• 削除したつもりが削除されていなかった問題

• StackOverflow問題

E C L I P S E L I N K版で調査間に合わず!

Page 118: はまる!!JPA #glassfish_jp #javaee

次回

はまる! J PA PA R T 2

へ続く・・・

Page 119: はまる!!JPA #glassfish_jp #javaee

重要なこと

• JPAを使う場合は、必ずSQLログを出力しよう。

• Entityのライフサイクルを把握しておこう。

• 根本解決を心がけよう。結果オーライな対処を続けていると「JPAこわい」で終わる

Page 120: はまる!!JPA #glassfish_jp #javaee

次回に続く ( ? )