...

SQL - Red Hat Customer Portal

by user

on
Category: Documents
182

views

Report

Comments

Transcript

SQL - Red Hat Customer Portal
JBoss Enterprise Application
Platform 4.2
Hibernate Reference Guide
for Use with JBoss Enterprise Application Platform 4.2
エディッション 1.0
Red Hat Documentation Group
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
for Use with JBoss Enterprise Application Platform 4.2
エディッション 1.0
Red Hat Do cumentatio n Gro up
法律上の通知
Copyright © 2010 Red Hat, Inc..
T his document is licensed by Red Hat under the Creative Commons Attribution-ShareAlike 3.0 Unported
License. If you distribute this document, or a modified version of it, you must provide attribution to Red
Hat, Inc. and provide a link to the original. If the document is modified, all Red Hat trademarks must be
removed.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section
4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, JBoss, MetaMatrix, Fedora, the Infinity Logo,
and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux ® is the registered trademark of Linus T orvalds in the United States and other countries.
Java ® is a registered trademark of Oracle and/or its affiliates.
XFS ® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States
and/or other countries.
MySQL ® is a registered trademark of MySQL AB in the United States, the European Union and other
countries.
Node.js ® is an official trademark of Joyent. Red Hat Software Collections is not formally related to or
endorsed by the official Joyent Node.js open source or commercial project.
T he OpenStack ® Word Mark and OpenStack Logo are either registered trademarks/service marks or
trademarks/service marks of the OpenStack Foundation, in the United States and other countries and
are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or
sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.
概要
T he Hibernate Reference Guide for use with JBoss Enterprise Application Platform 4.2 and its patch
releases.
目次
目次
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9. . . . . . . . . .
前書き
. . .1章
第
. . . .Hibernate
. . . . . . . . . . の導入
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
............
1.1. 前書き
10
1.2. パート1 - 初めての Hibernate アプリケーション
10
1.2.1. 最初のクラス
10
1.2.2. マッピングファイル
12
1.2.3. Hibernate の設定
14
1.2.4. Ant によるビルド
15
1.2.5. スタートアップとヘルパ
17
1.2.6. オブジェクトのロードと格納
18
1.3. パート2 - 関連のマッピング
21
1.3.1. Person クラスのマッピング
21
1.3.2. 単方向 Set ベース関連
22
1.3.3. 関連を働かせる
24
1.3.4. 値のコレクション
26
1.3.5. 双方向関連
27
1.3.6. 双方向リンクの動作
27
1.4. パート3 - EventManager Web アプリケーション
28
1.4.1. 基本的な Servlet の記述
28
1.4.2. 処理と描画
29
1.4.3. デプロイとテスト
31
1.5. 要約
32
. . .2章
第
. . . .アーキテクチャ
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .33
...........
2.1. 概観
33
2.2. インスタンスの状態
35
2.3. JMX との統合
36
2.4. JCA サポート
36
2.5. コンテキスト上のセッション
36
. . .3章
第
. . . 設定
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .38
...........
3.1. プログラム上の設定
38
3.2. SessionFactory を取得する
39
3.3. JDBC コネクション
39
3.4. オプション設定プロパティ
40
3.4.1. SQL 方言(Dialect)
47
3.4.2. 外部結合フェッチ
48
3.4.3. バイナリストリーム
49
3.4.4. ニ次キャッシュとクエリキャッシュ
49
3.4.5. クエリ言語の置き換え
49
3.4.6. Hibernate 統計
49
3.5. ロギング
49
3.6. NamingStrategy の実装
50
3.7. XML 設定ファイル
50
3.8. J2EE アプリケーションサーバーとの統合
51
3.8.1. トランザクション戦略設定
52
3.8.2. SessionFactory の JNDI への登録
53
3.8.3. JT A による現在のセッションコンテキストマネージメント
54
3.8.4. JMX デプロイメント
54
. . .4.章
第
. . .永続クラス
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
............
4.1. 単純な POJO の例
56
1
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
4.1.1. 引数のないコンストラクタを実装する
4.1.2. 識別子プロパティを用意する(オプション)
4.1.3. final クラスにしない(オプション)
4.1.4. 永続フィールドに対するアクセサとミューテータを定義する(オプション)
4.2. 継承の実装
4.3. equals() と hashCode() の実装
4.4. 動的モデル
4.5. T uplizer
58
58
58
59
59
59
60
62
. . .5章
第
. . . .基本的な
. . . . . . . . .O/R
. . . .マッピング
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
............
5.1. マッピング定義
64
5.1.1. Doctype
66
5.1.1.1. エンティティリゾルバ
66
5.1.2. hibernate-mapping
66
5.1.3. class
67
5.1.4. id
70
5.1.4.1. ジェネレータ
71
5.1.4.2. Hi/lo アルゴリズム
73
5.1.4.3. UUID アルゴリズム
73
5.1.4.4. 識別子カラムとシーケンス
73
5.1.4.5. 識別子の割り当て
73
5.1.4.6. トリガにより割り当てられた主キー
74
5.1.5. composite-id
74
5.1.6. discriminator
75
5.1.7. version(オプション)
76
5.1.8. timestamp(オプション)
77
5.1.9. property
77
5.1.10. many-to-one
79
5.1.11. one-to-one
81
5.1.12. natural-id
83
5.1.13. component, dynamic-component
84
5.1.14. properties
85
5.1.15. subclass
86
5.1.16. joined-subclass
86
5.1.17. union-subclass
88
5.1.18. join
89
5.1.19. key
90
5.1.20. column と formula 要素
91
5.1.21. import
91
5.1.22. any
92
5.2. Hibernate の型
93
5.2.1. エンティティと値
93
5.2.2. 基本的な型
93
5.2.3. カスタム型
95
5.3. 1つのクラスに1つ以上のマッピング
96
5.4. バッククォートで囲んだ SQL 識別子
96
5.5. メタデータの代替手段
97
5.5.1. XDoclet マークアップの使用
97
5.5.2. JDK 5.0 アノテーションの使用
99
5.6. 生成プロパティ
100
5.7. 補助的なデータベースオブジェクト
100
. . .6章
第
. . . .コレクションのマッピング
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
.............
6.1. コレクションの永続化
102
6.2. コレクションのマッピング
102
6.2.1. コレクションの外部キー
104
2
目次
6.2.2. コレクションの要素
6.2.3. インデックス付きのコレクション
6.2.4. 値のコレクションと多対多関連
6.2.5. 一対多関連
6.3. 高度なコレクションマッピング
6.3.1. ソートされたコレクション
6.3.2. 双方向関連
6.3.3. インデックス付きコレクションと双方向関連
6.3.4. 3項関連
6.3.5. Using an <idbag>
6.4. コレクションの例
104
104
105
107
108
108
109
111
112
112
113
. . .7章
第
. . . .関連マッピング
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
.............
7.1. イントロダクション
117
7.2. 単方向関連
117
7.2.1. 多対一
117
7.2.2. 一対一
117
7.2.3. 一対多
118
7.3. 結合テーブルを使った単方向関連
119
7.3.1. 一対多
119
7.3.2. 多対一
120
7.3.3. 一対一
120
7.3.4. 多対多
121
7.4. 双方向関連
121
7.4.1. 一対多/多対一
121
7.4.2. 一対一
122
7.5. 結合テーブルを使った双方向関連
123
7.5.1. 一対多/多対一
123
7.5.2. 一対一
124
7.5.3. 多対多
125
7.6. より複雑な関連マッピング
126
. . .8章
第
. . . .コンポーネントのマッピング
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
.............
8.1. 依存オブジェクト
128
8.2. 従属するオブジェクトのコレクション
130
8.3. Map のインデックスとしてのコンポーネント
131
8.4. 複合識別子としてのコンポーネント
131
8.5. 動的コンポーネント
133
. . .9章
第
. . . .継承マッピング
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
.............
9.1. 3つの戦略
134
9.1.1. クラス階層ごとのテーブル(table-per-class-hierarchy)
134
9.1.2. サブクラスごとのテーブル (table-per-subclass)
135
9.1.3. discriminator を用いた table-per-subclass
135
9.1.4. table-per-subclass と table-per-class-hierarchy の混合
136
9.1.5. 具象クラスごとのテーブル(table-per-concrete-class)
137
9.1.6. 暗黙的ポリモーフィズムを用いた table-per-concrete-class
137
9.1.7. 他の継承マッピングと暗黙的ポリモーフィズムの組み合わせ
138
9.2. 制限
139
. . .10章
第
. . . . .オブジェクトを扱う
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .14
. . .1. . . . . . . . . .
10.1. Hibernate におけるオブジェクトの状態
141
10.2. オブジェクトを永続状態にする
141
10.3. オブジェクトのロード
142
10.4. クエリ
143
10.4.1. クエリの実行
143
3
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
10.4.1.1. 結果をイテレートする
10.4.1.2. オブジェクトの組(tuple)を返すクエリ
10.4.1.3. スカラーの結果
10.4.1.4. パラメータのバインド
10.4.1.5. ページ分け
10.4.1.6. スクロール可能なイテレーション
10.4.1.7. 名前付きクエリの外出し
10.4.2. フィルタリングコレクション
10.4.3. クライテリアのクエリ
10.4.4. ネイティブ SQL のクエリ
10.5. 永続オブジェクトの修正
10.6. detached オブジェクトの修正
10.7. 自動的な状態検出
10.8. 永続オブジェクトの削除
10.9. 異なる二つのデータストア間でのオブジェクトのレプリケーション
10.10. セッションのフラッシュ
10.11. 連鎖的な永続化
10.12. メタデータの使用
144
144
145
145
146
146
146
147
147
148
148
149
150
151
151
151
152
154
. . .11章
第
. . . . .トランザクションと並行性
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
.............
11.1. session スコープと transaction スコープ
155
11.1.1. 作業単位(Unit of work)
155
11.1.2. 長い対話
156
11.1.3. オブジェクト識別子を考える
157
11.1.4. 一般的な問題
158
11.2. データベーストランザクション境界
158
11.2.1. 管理されていない環境
159
11.2.2. JT A を使用する
160
11.2.3. 例外ハンドリング
161
11.2.4. トランザクションのタイムアウト
162
11.3. 楽観的同時実行制御
163
11.3.1. アプリケーションによるバージョンチェック
163
11.3.2. 拡張セッションと自動バージョニング
164
11.3.3. デタッチされたオブジェクトと自動バージョニング
165
11.3.4. 自動バージョニングのカスタマイズ
165
11.4. 悲観的ロック
166
11.5. コネクション開放モード
166
. . .12章
第
. . . . .インターセプタとイベント
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
.............
12.1. インターセプタ
168
12.2. イベントシステム
170
12.3. Hibernate の宣言的なセキュリティ
171
. . .13章
第
. . . . .バッチ処理
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
.............
13.1. バッチ挿入
173
13.2. バッチ更新
173
13.3. StatelessSession インターフェース
174
13.4. DML スタイルの操作
175
. . .14
第
. .章
. . .HQL:
. . . . . .Hibernate
. . . . . . . . . . クエリ言語
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
.............
14.1. 大文字と小文字の区別
178
14.2. from 節
178
14.3. 関連と結合
178
14.4. 結合構文の形式
180
14.5. 識別子プロパティの参照
180
14.6. Select 節
180
4
目次
14.7. 集約関数
14.8. ポリモーフィックなクエリ
14.9. where 節
14.10. Expressions 式
14.11. order by 節
14.12. group by 節
14.13. 副問い合わせ
14.14. HQL の例
14.15. 大量の UPDAT E と DELET E
14.16. T ips & T ricks
14.17. コンポーネント
14.18. 行値コンストラクタ構文
181
182
182
184
187
187
188
188
190
191
192
192
. . .15章
第
. . . . .Criteria
. . . . . . . . クエリ
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .194
.............
15.1. Criteria インスタンスの作成
194
15.2. リザルトセットの絞込み
194
15.3. 結果の整列
195
15.4. 関連
195
15.5. 関連の動的フェッチ
196
15.6. クエリの例
196
15.7. 射影、集約、グループ化
197
15.8. クエリおよびサブクエリの分離
198
15.9. 自然識別子によるクエリ
199
. . .16章
第
. . . . .ネイティブ
. . . . . . . . . . . SQL
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
.............
16.1. SQLQuery の使用
201
16.1.1. スカラーのクエリ
201
16.1.2. エンティティのクエリ
202
16.1.3. 関連とコレクションの操作
202
16.1.4. 複数エンティティの取得
203
16.1.4.1. 別名とプロパティのリファレンス
203
16.1.5. 管理されていないエンティティの取得
204
16.1.6. 継承の制御
204
16.1.7. パラメータ
204
16.2. 名前付き SQL クエリ
205
16.2.1. 列と列の別名を明示的に指定するために return-property を使う
206
16.2.2. 問い合わせするためにストアドプロシージャを使う
207
16.2.2.1. ストアドプロシージャを使う上でのルールと制限
208
16.3. 作成、更新、削除のためのカスタム SQL
208
16.4. ロードのためのカスタム SQL
209
. . .17章
第
. . . . .データのフィルタリング
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
.............
17.1. Hibernate のフィルタ
211
. . .18章
第
. . . . .XML
. . . . .マッピング
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
.............
18.1. XML データでの作業
213
18.1.1. XML とクラスのマッピングを同時に指定する
213
18.1.2. XML マッピングだけを指定する
213
18.2. XML マッピングのメタデータ
214
18.3. XML データを扱う
216
. . .19章
第
. . . . .パフォーマンスの改善
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
.............
19.1. フェッチ戦略
218
19.1.1. 遅延関連の働き
218
19.1.2. フェッチ戦略のチューニング
219
19.1.3. 単一端関連プロキシ
220
19.1.4. コレクションとプロキシの初期化
221
5
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
19.1.5. バッチフェッチの使用
19.1.6. サブセレクトフェッチの使用
19.1.7. 遅延プロパティフェッチの使用
19.2. 第2レベルキャッシュ
19.2.1. キャッシュのマッピング
19.2.2. read only 戦略
19.2.3. read/write 戦略
19.2.4. 厳密ではない read/write 戦略
19.2.5. transactional 戦略
19.3. キャッシュの管理
19.4. クエリキャッシュ
19.5. コレクションのパフォーマンスの理解
19.5.1. 分類
19.5.2. 更新にもっとも効率的なコレクション list、map、idbag、set
19.5.3. inverse コレクションにもっとも最適な bag と list
19.5.4. 一括削除
19.6. パフォーマンスのモニタリング
19.6.1. SessionFactory のモニタリング
19.6.2. メトリクス
222
223
223
224
225
225
226
226
226
227
228
229
229
229
230
230
230
231
231
. . .20章
第
. . . . .ツールセットガイド
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
.............
20.1. スキーマの自動生成
233
20.1.1. スキーマのカスタマイズ
233
20.1.2. ツールの実行
236
20.1.3. プロパティ
236
20.1.4. Ant を使用する
237
20.1.5. インクリメンタルなスキーマ更新
237
20.1.6. インクリメンタルなスキーマ更新に対する Ant の使用
238
20.1.7. Schema validation
238
20.1.8. スキーマのバリデーションに Ant を使用します
239
. . .21章
第
. . . . .例:
. . . . .親
. ./子供
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .24
. . .0. . . . . . . . . .
21.1. コレクションに関する注意
240
21.2. 双方向一対多
240
21.3. ライフサイクルのカスケード
242
21.4. カスケードと unsaved-value
242
21.5. 結論
243
. . .22章
第
. . . . .例
. .:. Weblog
. . . . . . . . アプリケーション
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .24
. . .4. . . . . . . . . .
22.1. 永続クラス
244
22.2. Hibernate のマッピング
245
22.3. Hibernate のコード
247
. . .23章
第
. . . . .例:
. . . . .いろいろなマッピング
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
.............
23.1. 雇用者/従業員
252
23.2. 作者/作品
254
23.3. 顧客/注文/製品
256
23.4. 種々雑多なマッピング例
258
23.4.1. "T yped" one-to-one association
258
23.4.2. 複合キーの例
259
23.4.3. 複合キー属性を共有する多対多
261
23.4.4. discrimination に基づく内容
262
23.4.5. 代替キーの関連
263
. . .24
第
. .章
. . .ベストプラクティス
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
.............
. . . . . . . . . .History
Revision
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
.............
6
目次
7
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
8
前書き
前書き
Working with object-oriented software and a relational database can be cumbersome and time
consuming in today's enterprise environments. Hibernate is an object/relational mapping tool for Java
environments. T he term object/relational mapping (ORM) refers to the technique of mapping a data
representation from an object model to a relational data model with a SQL-based schema.
Hibernate は Java クラスからデータベーステーブルへのマッピング(及び Java データタイプから SQL
データタイプへのマッピング)を行うだけでなくデータのクエリや検索機能も提供するため、 SQL や
JDBC での手作業によるデータ処理を除き開発に要する時間を大幅に削減することが可能になります。
Hibernate の目標は、 開発者にとってのプログラミングにおける一般的なデータ永続性の作業の 95 % を
軽減することです。 Hibernate データベース内でビジネスロジックを実現するストアドプロシージャのみ
を使用するデータ処理中心のアプリケーションに対しては最適ではないかもしれませんが、 Java ベース
の中間層でのビジネスロジック及びオブジェクト指向のドメインモデルを使用する場合に最も役に立ちま
す。 Hibernate は開発者がベンダー固有の SQL コードの除去あるいはカプセル化を行う際に便利なた
め、 表形式の表現からオブジェクトのグラフへの結果セットの変換に関する一般的な作業に役立ちます。
Hibernate 及びオブジェクト/リレーショナルマッピング、 あるいは Java が不慣れな方は、 次の手順を
行ってください。
1. Read 1章Hibernate の導入 for a tutorial with step-by-step instructions. T he source code for the
tutorial is included in the distribution in the doc/reference/tutorial/ directory.
2. Read 2章アーキテクチャ to understand the environments where Hibernate can be used.
3. Hibernate ディストリビューション内の eg/ ディレクトリ内を見てください。 シンプルなスタンド
アローンのアプリケーションが含まれています。 ご使用の JDBC ドライバを lib/ ディレクトリに
コピーしてから使用するデータベースに対して正しい値を指定するよう
etc/hibernate.properties を編集します。 ディストリビューションディレクトリ内のコマン
ドプロンプトから、 ant eg (Ant を使用)と入力するか、 Windows 環境の場合は build eg と入
力します。
4. おもな情報源として本リファレンスドキュメントをご利用ください。 アプリケーションのデザイン
に関する詳細、 ステップバイステップによる解説が必要な場合は、 Hibernate in Action
(http://www.manning.com/bauer) をお読みになってもいいでしょう。 また、
http://caveatemptor.hibernate.org から Hibernate in Action のサンプルアプリケーションをダウン
ロードすることもできます。
5. よくある質問とその答え (FAQ) は Hibernate ウェブサイトでご覧ください。
6. サードパーティのデモ、 サンプル、 チュートリアルなどは Hibernate のウェブサイト上にリンクさ
れています。
7. Hibernate ウェブサイト上の Community Area はデザインのパターンやさまざまな統合ソリュー
ション (T omcat、 JBoss AS、 Struts、 EJB など)を検索する上で興味深いリソースになります。
質問がある場合は、 Hibernate ウェブサイト上にリンクされたユーザーフォーラムをご利用ください。 ま
た、 バグ報告及び今後のリクエストに関しては JIRA (問題追跡システム) を提供しています。 Hibernate,
の開発に興味がある方は、 開発者用メーリングリストにご参加ください。 本ドキュメントの翻訳に興味
がある方は、 開発者用メーリングリストよりご連絡ください。
Hibernate に関する商業用開発サポート、 実稼働サポート、 トレーニングについては JBoss Inc よりご利
用頂けます (http://www.hibernate.org/SupportT raining/ を参照)。 Hibernate はプロフェッショナルなオー
プンソースプロジェクトであり、 JBoss Enterprise Middleware System (JEMS) スィート製品の重要なコ
ンポーネントになります。
9
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 1章 Hibernate の導入
1.1. 前 書 き
この章は Hibernate を初めて使うユーザー向けの入門的なチュートリアルです。インメモリデータベース
を使う簡単なコマンドラインアプリケーションから始め、一歩一歩わかりやすいやり方で開発を進めま
す。
このチュートリアルは Hibernate を初めて使うユーザーを想定していますが、理解するには Java と SQL
についての知識が必要です。これは Michael Gloegl の手によるチュートリアルを下敷きにしていますが、
ここでサードパーティライブラリと述べているのは、 JDK 1.4 と 5.0 用のものです。 JDK 1.3 を利用する
のであれば他のライブラリが必要かもしれません。
チュートリアルのソースコードは Hibernate ディストリビューションの doc/reference/tutorial/
にあります。
1.2. パ ー ト 1 - 初 め て の Hibernate ア プ リ ケ ー シ ョ ン
First, we'll create a simple console-based Hibernate application. We use an Java database (HSQL DB),
so we do not have to install any database server.
Let's assume we need a small database application that can store events we want to attend, and
information about the hosts of these events.
まず最初にすることは開発用のディレクトリをセットアップして、必要となるすべての Java ライブラリ
を配置することです。 Hibernate ウェブサイトから Hibernate ディストリビューションをダウンロードし
てください。ファイルを解凍して /lib にある必要なライブラリのすべてを、新しい開発用ディレクトリ
の /lib ディレクトリに配置してください。このようになっているはずです:
.
+lib
antlr.jar
cglib.jar
asm.jar
asm-attrs.jars
commons-collections.jar
commons-logging.jar
hibernate3.jar
jta.jar
dom4j.jar
log4j.jar
これが 本ドキュメント執筆時点での Hibernate の必要最低限のライブラリです (メインアーカイブの
hibernate3.jar もコピーしていることに注意してください)。 Hibernate のバージョンによってはさらに
必要なライブラリや、不要なライブラリがあるかもしれません。 Hibernate ディストリビューションの
lib/ ディレクトリにある README.txt ファイルを見てください。必須またはオプションのサードパー
ティライブラリについての情報を載せています (実際 Log4j は必須ではありませんが、多くの開発者が好
んでいます)。
次にデータベースに格納するイベントを表すクラスを作成します。
1.2.1. 最初のクラス
最初の永続クラスは、プロパティをいくつか持つシンプルな JavaBean です:
10
第1章 Hibernate の導入
package events;
import java.util.Date;
public class Event {
private Long id;
private String title;
private Date date;
public Event() {}
public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
ご覧のとおり、このクラスはフィールドが private の可視性を持っているのと同時に、 JavaBean 標準の
ゲッター、セッターメソッドの命名規約に従っています。このような設計は推奨されていますが必須では
ありません。アクセサメソッドを設けるのはリファクタリングを考えた頑健性のためで、 Hibernate は
フィールドに直接アクセスすることも可能です。引数のないコンストラクタは、リフレクションでこのク
ラスのインスタンスを作成するために必要です。
T he id property holds a unique identifier value for a particular event. All persistent entity classes (there
are less important dependent classes as well) will need such an identifier property if we want to use the
full feature set of Hibernate. In fact, most applications (esp. web applications) need to distinguish objects
by identifier, so you should consider this a feature rather than a limitation. However, we usually don't
manipulate the identity of an object, hence the setter method should be private. Only Hibernate will
assign identifiers when an object is saved. You can see that Hibernate can access public, private, and
protected accessor methods, as well as (public, private, protected) fields directly. T he choice is up to you
and you can match it to fit your application design.
引数のないコンストラクタはすべての永続クラスに必須です。これは Hibernate が Java のリフレクショ
ンを使って、オブジェクトを作成しなければならないためです。コンストラクタを private にすることは
可能ですが、実行時のプロキシ生成と、バイトコード操作なしの効率的なデータの抽出には、 package 可
視性が必要です。
11
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
開発フォルダの src というディレクトリの適切なパッケージに、この Java ソースファイルを配置してく
ださい。この時点でディレクトリは以下のようになっているはずです:
.
+lib
<Hibernate and third-party libraries>
+src
+events
Event.java
次のステップでは、 Hibernate にこの永続クラスの情報を教えます。
1.2.2. マッピングファイル
Hibernate は、どのように永続クラスのオブジェクトをロードし格納すればよいかを知る必要がありま
す。ここで Hibernate マッピングファイルが登場します。マッピングファイルは、データベース内のどの
テーブルにアクセスしなければならないか、そのテーブルのどのカラムを使うべきかを、 Hibernate に教
えます。
マッピングファイルの基本的な構造はこのようになります:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
[...]
</hibernate-mapping>
Note that the Hibernate DT D is very sophisticated. You can use it for auto-completion of XML mapping
elements and attributes in your editor or IDE. You also should open up the DT D file in your text editor it's the easiest way to get an overview of all elements and attributes and to see the defaults, as well as
some comments. Note that Hibernate will not load the DT D file from the web, but first look it up from the
classpath of the application. T he DT D file is included in hibernate3.jar as well as in the src/
directory of the Hibernate distribution.
以降の例ではコードを短くするために DT D 宣言を省略します。当然ですがこれはオプションではありま
せん。
2つの hibernate-m apping タグの間に class 要素を含めてください。すべての永続エンティティク
ラス(念を押しますが、ファーストクラスのエンティティではない依存クラスというものが後ほど登場し
ます)は SQL データベース内のテーブルへのこのようなマッピングを必要とします。
<hibernate-mapping>
<class name="events.Event" table="EVENTS">
</class>
</hibernate-mapping>
So far we told Hibernate how to persist and load object of class Event to the table EVENT S, each
instance represented by a row in that table. Now we continue with a mapping of the unique identifier
property to the tables primary key. In addition, as we don't want to care about handling this identifier, we
configure Hibernate's identifier generation strategy for a surrogate primary key column:
12
第1章 Hibernate の導入
<hibernate-mapping>
<class name="events.Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
</class>
</hibernate-mapping>
T he id element is the declaration of the identifer property, nam e="id" declares the name of the Java
property - Hibernate will use the getter and setter methods to access the property. T he column attribute
tells Hibernate which column of the EVENT S table we use for this primary key. T he nested generator
element specifies the identifier generation strategy, in this case we used native, which picks the best
strategy depending on the configured database (dialect). Hibernate supports database generated,
globally unique, as well as application assigned identifiers (or any strategy you have written an
extension for).
最後にクラスの永続プロパティの宣言をマッピングファイルに含めます。デフォルトでは、クラスのプロ
パティは永続と見なされません:
<hibernate-mapping>
<class name="events.Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title"/>
</class>
</hibernate-mapping>
id 要素の場合と同様に、 property 要素の nam e 属性で、どのゲッターとセッターメソッドを使うべき
かを Hibernate に教えます。この例では、 Hibernate は getDate()/setDate() と
getT itle()/setT itle() を探します。
Why does the date property mapping include the colum n attribute, but the title doesn't? Without the
colum n attribute Hibernate by default uses the property name as the column name. T his works fine for
title. However, date is a reserved keyword in most database, so we better map it to a different name.
T he next interesting thing is that the title mapping also lacks a type attribute. T he types we declare
and use in the mapping files are not, as you might expect, Java data types. T hey are also not SQL
database types. T hese types are so called Hibernate mapping types, converters which can translate
from Java to SQL data types and vice versa. Again, Hibernate will try to determine the correct conversion
and mapping type itself if the type attribute is not present in the mapping. In some cases this automatic
detection (using Reflection on the Java class) might not have the default you expect or need. T his is the
case with the date property. Hibernate can't know if the property (which is of java.util.Date) should
map to a SQL date, tim estam p, or tim e column. We preserve full date and time information by
mapping the property with a tim estam p converter.
このマッピングファイルは、 Event.hbm .xm l として Event Java クラスソースファイルのすぐ隣に
セーブするべきです。マッピングファイルの命名方法は任意ですが、 hbm .xm l サフィックスが
Hibernate の開発者のコミュニティ内での習慣となっています。現在ディレクトリ構造は以下のように
なっているはずです:
13
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
.
+lib
<Hibernate and third-party libraries>
+src
+events
Event.java
Event.hbm.xml
Hibernate の主要な設定を続けます。
1.2.3. Hibernate の設定
ここまでで永続クラスとマッピングファイルが揃いました。これから Hibernate の設定を行いますが、そ
の前にデータベースが必要です。 HSQL DB は Java ベースのインメモリ SQL DBMS であり、 HSQL DB
ウェブサイトからダウンロードできます。実際にはダウンロードした中の hsqldb.jar だけが必要で
す。このファイルを開発フォルダの lib/ ディレクトリに配置してください。
data というディレクトリを開発ディレクトリのルートに作成してください。 HSQL DB はここにデータ
ファイルを格納します。このデータディレクトリにおいて java -classpath ../lib/hsqldb.jar
org.hsqldb.Server を実行し、データベースを起動させてください。動作の開始と、 T CP/IP ソケッ
トのバインドが確認できます。後ほど作成するアプリケーションはここに接続します。もしこのチュート
リアル中にデータベースを初期化したければ、 HSQL DB をシャットダウンして(作業ウィンドウで
CT RL + C を押します) data/ ディレクトリ内のファイルを全て消去した後、 HSQL DB を再起動しま
す。
Hibernate はアプリケーションのデータベースに接続する層なので、コネクションの情報が必要になりま
す。コネクションは JDBC コネクションプールを通じて行われますが、これも設定する必要があります。
Hibernate ディストリビューションにはいくつかのオープンソースの JDBC コネクションプールツールが
含まれていますが、このチュートリアルでは Hibernate に組み込まれたコネクションプールを使います。
もし製品レベルの品質のサードパーティ JDBC コネクションプールソフトウェアを使いたければ、クラス
パスに必要なライブラリをコピーして、異なるコネクションプールを設定しなければならないことに注意
してください。
For Hibernate's configuration, we can use a simple hibernate.properties file, a slightly more
sophisticated hibernate.cfg.xm l file, or even complete programmatic setup. Most users prefer the
XML configuration file:
14
第1章 Hibernate の導入
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property
name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<mapping resource="events/Event.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Note that this XML configuration uses a different DT D. We configure Hibernate's SessionFactory - a
global factory responsible for a particular database. If you have several databases, use several
<session-factory> configurations, usually in several configuration files (for easier startup).
T he first four property elements contain the necessary configuration for the JDBC connection. T he
dialect property element specifies the particular SQL variant Hibernate generates. Hibernate's
automatic session management for persistence contexts will come in handy as you will soon see. T he
hbm 2ddl.auto option turns on automatic generation of database schemas - directly into the database.
T his can of course also be turned off (by removing the config option) or redirected to a file with the help
of the Schem aExport Ant task. Finally, we add the mapping file(s) for persistent classes to the
configuration.
このファイルをソースディレクトリにコピーしてください。するとこれはクラスパスのルートにあること
になります。 Hibernate は、スタートアップ時にクラスパスのルートで hibernate.cfg.xm l という
ファイルを自動的に探します。
1.2.4. Ant によるビルド
15
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
We'll now build the tutorial with Ant. You will need to have Ant installed - get it from the Ant download
page. How to install Ant will not be covered here. Please refer to the Ant manual. After you have installed
Ant, we can start to create the buildfile. It will be called build.xm l and placed directly in the
development directory.
基本的なビルドファイルはこのようになります:
<project name="hibernate-tutorial" default="compile">
<property name="sourcedir" value="${basedir}/src"/>
<property name="targetdir" value="${basedir}/bin"/>
<property name="librarydir" value="${basedir}/lib"/>
<path id="libraries">
<fileset dir="${librarydir}">
<include name="*.jar"/>
</fileset>
</path>
<target name="clean">
<delete dir="${targetdir}"/>
<mkdir dir="${targetdir}"/>
</target>
<target name="compile" depends="clean, copy-resources">
<javac srcdir="${sourcedir}"
destdir="${targetdir}"
classpathref="libraries"/>
</target>
<target name="copy-resources">
<copy todir="${targetdir}">
<fileset dir="${sourcedir}">
<exclude name="**/*.java"/>
</fileset>
</copy>
</target>
</project>
これは .jar で終わる lib ディレクトリのすべてのファイルを、コンパイルに使用するクラスパスに追加
することを Ant に教えます。また、 Java ソースファイルでないすべてのファイルをターゲットディレク
トリにコピーするということでもあります。例えば設定ファイルや Hibernate マッピングファイルなどで
す。今 Ant を実行すると、このような出力があるはずです:
C:\hibernateTutorial\>ant
Buildfile: build.xml
copy-resources:
[copy] Copying 2 files to C:\hibernateTutorial\bin
compile:
[javac] Compiling 1 source file to C:\hibernateTutorial\bin
BUILD SUCCESSFUL
Total time: 1 second
16
第1章 Hibernate の導入
1.2.5. スタートアップとヘルパ
It's time to load and store some Event objects, but first we have to complete the setup with some
infrastructure code. We have to startup Hibernate. T his startup includes building a global
SessionFactory object and to store it somewhere for easy access in application code. A
SessionFactory can open up new Session's. A Session represents a single-threaded unit of work,
the SessionFactory is a thread-safe global object, instantiated once.
We'll create a HibernateUtil helper class which takes care of startup and makes accessing a
SessionFactory convenient. Let's have a look at the implementation:
package util;
import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
このクラスは静的初期化ブロック(クラスがロードされるときに JVM によって一度だけ呼ばれる) でグ
ローバルの SessionFactory を生成するだけではなく、静的シングルトンの使用を隠蔽します。アプリ
ケーションサーバーの JNDI から SessionFactory をルックアップするのと同様です。
設定ファイル内で SessionFactory に名前を与えると、 Hibernate は SessionFactory 構築後に
JNDI に対しバインドを行おうとします。このコードを完全に排除するためには、 JMX デプロイメントを
利用して JMX を利用できるコンテナをインスタンス化し、 HibernateService を JNDI へバインドす
ることもできます。これらの高度なオプションは、 Hibernate のリファレンスドキュメントで説明されて
います。
HibernateUtil.java を開発ソースディレクトリにある events パッケージの隣に配置してくださ
い。
17
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
.
+lib
<Hibernate and third-party libraries>
+src
+events
Event.java
Event.hbm.xml
+util
HibernateUtil.java
hibernate.cfg.xml
+data
build.xml
T his should again compile without problems. We finally need to configure a logging system - Hibernate
uses commons logging and leaves you the choice between Log4j and JDK 1.4 logging. Most developers
prefer Log4j: copy log4 j.properties from the Hibernate distribution (it's in the etc/ directory) to
your src directory, next to hibernate.cfg.xm l. Have a look at the example configuration and
change the settings if you like to have more verbose output. By default, only Hibernate startup message
are shown on stdout.
チュートリアルのインフラは完全です。 Hibernate を使って実際の作業をする準備が整いました。
1.2.6. オブジェクトのロードと格納
ついにオブジェクトのロードと格納に Hibernate を使うことができます。 m ain() メソッドを持つ
EventManager クラスを書きます:
18
第1章 Hibernate の導入
package events;
import org.hibernate.Session;
import java.util.Date;
import util.HibernateUtil;
public class EventManager {
public static void main(String[] args) {
EventManager mgr = new EventManager();
if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
HibernateUtil.getSessionFactory().close();
}
private void createAndStoreEvent(String title, Date theDate) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
session.save(theEvent);
session.getTransaction().commit();
}
}
We create a new Event object, and hand it over to Hibernate. Hibernate now takes care of the SQL and
executes INSERT s on the database. Let's have a look at the Session and T ransaction-handling
code before we run this.
A Session is a single unit of work. For now we'll keep things simple and assume a one-to-one
granularity between a Hibernate Session and a database transaction. T o shield our code from the
actual underlying transaction system (in this case plain JDBC, but it could also run with JT A) we use the
T ransaction API that is available on the Hibernate Session.
What does sessionFactory.getCurrentSession() do? First, you can call it as many times and
anywhere you like, once you get hold of your SessionFactory (easy thanks to HibernateUtil).
T he getCurrentSession() method always returns the "current" unit of work. Remember that we
switched the configuration option for this mechanism to "thread" in hibernate.cfg.xm l? Hence, the
current unit of work is bound to the current Java thread that executes our application. However, this is
not the full picture, you also have to consider scope, when a unit of work begins and when it ends.
A Session begins when it is first needed, when the first call to getCurrentSession() is made. It is
then bound by Hibernate to the current thread. When the transaction ends, either through commit or
rollback, Hibernate automatically unbinds the Session from the thread and closes it for you. If you call
getCurrentSession() again, you get a new Session and can start a new unit of work. T his threadbound programming model is the most popular way of using Hibernate, as it allows flexible layering of
19
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
your code (transaction demarcation code can be separated from data access code, we'll do this later in
this tutorial).
作業単位 (Unit of Work) の範囲に関して、 Hibernate の Session は1つまたはいくつかのデータベースオ
ペレーションを実行するために使用されるべきでしょうか?上記の例は、1つのオペレーションで1つの
Session を使用します。これは純粋な偶然で、例はその他のアプローチを示すほど込み入っていませ
ん。 Hibernate の Session の範囲は柔軟ですが、 全ての データベースオペレーションのために新しい
Hibernate Session を使用するようにアプリケーションをデザインするべきではありません。従って、も
しそれを以下の (普通の) 例で何度か見たとしても、アンチパターンである オペレーション毎の Session
を考慮してください。実際の (ウェブ) アプリケーションは、このチュートリアルで後に見ることができま
す。
Have a look at 11章トランザクションと並行性 for more information about transaction handling and
demarcation. We also skipped any error handling and rollback in the previous example.
この最初のルーチンを実行するには、 Ant のビルドファイルに呼び出し可能なターゲットを追加しなけれ
ばなりません:
<target name="run" depends="compile">
<java fork="true" classname="events.EventManager" classpathref="libraries">
<classpath path="${targetdir}"/>
<arg value="${action}"/>
</java>
</target>
action 引数の値は、ターゲットを呼ぶときにコマンドラインで設定します:
C:\hibernateTutorial\>ant run -Daction=store
コンパイルすると、 Hibernate がスタートし、設定によりますが、多くのログ出力があるはずです。その
最後には以下の行があるでしょう:
[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)
これは Hibernate が実行する INSERT で、クエスチョンマークは JDBC バインドパラメータを表してい
ます。引数としてバインドされる値を見るため、あるいはログの冗長性を減らすためには、
log4 j.properties をチェックしてください。
Now we'd like to list stored events as well, so we add an option to the main method:
if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
else if (args[0].equals("list")) {
List events = mgr.listEvents();
for (int i = 0; i < events.size(); i++) {
Event theEvent = (Event) events.get(i);
System.out.println("Event: " + theEvent.getTitle() +
" Time: " + theEvent.getDate());
}
}
新しい listEvents()メソッド も追加します。
20
第1章 Hibernate の導入
private List listEvents() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
List result = session.createQuery("from Event").list();
session.getTransaction().commit();
return result;
}
ここですることは、データベースから存在するすべての Event オブジェクトをロードする HQL
(Hibernate Query Language) クエリを使うことです。 Hibernate は適切な SQL を生成し、それをデータ
ベースに送り、そのデータを使って Event オブジェクトを生成します。当然 HQL でさらに複雑なクエリ
を作成できます。
以下のステップで、すべての実行とテストを行います:
hbm2ddl を通す前にデータベースのデータを作成し、データベーススキーマを生成するために、 ant
run -Daction=store を実行してください。
Now disable hbm2ddl by commenting out the property in your hibernate.cfg.xm l file. Usually
you only leave it turned on in continous unit testing, but another run of hbm2ddl would drop
everything you have stored - the create configuration setting actually translates into "drop all
tables from the schema, then re-create all tables, when the SessionFactory is build".
今 -Daction=list と指定して Ant を呼ぶと、これまで格納したイベントが見えるはずです。 store ア
クションを数回以上呼ぶことも可能です。
注記:初めて Hibernate に触れる人々の多くがここで失敗するため、 Table not found エラーメッセージ
に関する質問を定期的に見かけます。しかし上記のステップに従えば、 hbm2ddl が最初に実行されたとき
にデータベーススキーマを作成し、その後の実行においてもこのスキーマを使用するので、問題は起こら
ないでしょう。マッピングやデータベーススキーマを変更したときは、もう一度 hbm2ddl を有効にしてく
ださい。
1.3. パ ー ト 2 - 関 連 の マ ッ ピ ン グ
We mapped a persistent entity class to a table. Let's build on this and add some class associations.
First we'll add people to our application, and store a list of events they participate in.
1.3.1. Person クラスのマッピング
最初の Person クラスは単純です:
21
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
package events;
public class Person {
private
private
private
private
Long id;
int age;
String firstname;
String lastname;
public Person() {}
// Accessor methods for all properties, private setter for 'id'
}
Create a new mapping file called Person.hbm .xm l (don't forget the DT D reference at the top):
<hibernate-mapping>
<class name="events.Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>
<property name="age"/>
<property name="firstname"/>
<property name="lastname"/>
</class>
</hibernate-mapping>
Finally, add the new mapping to Hibernate's configuration:
<mapping resource="events/Event.hbm.xml"/>
<mapping resource="events/Person.hbm.xml"/>
We'll now create an association between these two entities. Obviously, persons can participate in
events, and events have participants. T he design questions we have to deal with are: directionality,
multiplicity, and collection behavior.
1.3.2. 単方向 Set ベース関連
We'll add a collection of events to the Person class. T hat way we can easily navigate to the events for
a particular person, without executing an explicit query - by calling aPerson.getEvents(). We use a
Java collection, a Set, because the collection will not contain duplicate elements and the ordering is not
relevant for us.
We need a unidirectional, many-valued associations, implemented with a Set. Let's write the code for
this in the Java classes and then map it:
22
第1章 Hibernate の導入
public class Person {
private Set events = new HashSet();
public Set getEvents() {
return events;
}
public void setEvents(Set events) {
this.events = events;
}
}
Before we map this association, think about the other side. Clearly, we could just keep this unidirectional.
Or, we could create another collection on the Event, if we want to be able to navigate it bi-directional, i.e.
anEvent.getParticipants(). T his is not necessary, from a functional perspective. You could
always execute an explicit query to retrieve the participants for a particular event. T his is a design
choice left to you, but what is clear from this discussion is the multiplicity of the association: "many"
valued on both sides, we call this a many-to-many association. Hence, we use Hibernate's many-tomany mapping:
<class name="events.Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>
<property name="age"/>
<property name="firstname"/>
<property name="lastname"/>
<set name="events" table="PERSON_EVENT">
<key column="PERSON_ID"/>
<many-to-many column="EVENT_ID" class="events.Event"/>
</set>
</class>
Hibernate supports all kinds of collection mappings, a <set> being most common. For a many-to-many
association (or n:m entity relationship), an association table is needed. Each row in this table represents
a link between a person and an event. T he table name is configured with the table attribute of the set
element. T he identifier column name in the association, for the person's side, is defined with the <key>
element, the column name for the event's side with the colum n attribute of the <m any-to-m any>. You
also have to tell Hibernate the class of the objects in your collection (correct: the class on the other side
of the collection of references).
そのためこのマッピングのデータベーススキーマは以下のようになります:
23
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
_____________
__________________
|
|
|
|
_____________
|
EVENTS
|
|
PERSON_EVENT
|
|
|
|_____________|
|__________________|
|
PERSON
|
|
|
|
|
|_____________|
| *EVENT_ID
| <--> | *EVENT_ID
|
|
|
| EVENT_DATE |
| *PERSON_ID
| <--> | *PERSON_ID |
| TITLE
|
|__________________|
| AGE
|
|_____________|
| FIRSTNAME |
| LASTNAME
|
|_____________|
1.3.3. 関連を働かせる
Let's bring some people and events together in a new method in EventManager:
private void addPersonToEvent(Long personId, Long eventId) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Person aPerson = (Person) session.load(Person.class, personId);
Event anEvent = (Event) session.load(Event.class, eventId);
aPerson.getEvents().add(anEvent);
session.getTransaction().commit();
}
Person と Event をロードした後、普通のコレクションメソッドを使って単純にそのコレクションを修
正してください。ご覧のとおり update() や save() の明示的な呼び出しはありません。 Hibernate
は、修正されたことにより更新する必要のあるコレクションを自動的に検知します。これは 自動ダーティ
チェック と呼ばれ、オブジェクトの名前や date プロパティを修正することで試すことも可能です。それ
らが 永続 状態にある限り、つまり特定の Hibernate Session にバインドされている限り (例えば作業
単位 (Unit of Work) の中で単にロードまたはセーブされた)、 Hibernate はどんな変更もモニターし、遅
延書き込み (write-behind) で SQL を実行します。通常、作業単位 (Unit of Work) の最後にだけ行われる
データベースとメモリの状態を同期させる処理は、 フラッシュ と呼ばれます。このコードでは、作業単
位 (Unit of Work) はデータベーストランザクションのコミット(もしくはロールバック)で終了します。
これは、 CurrentSessionContext クラスに対して thread を設定したためです。
異なる作業単位 (Unit of Work) で人々とイベントをロードすることも当然できます。そうでなければ、永
続状態にないとき(以前に永続であったなら、この状態を 分離(detached) と呼びます)、 Session
の外部でオブジェクトを修正します。分離されるときにはコレクションを変更することも可能です:
24
第1章 Hibernate の導入
private void addPersonToEvent(Long personId, Long eventId) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Person aPerson = (Person) session
.createQuery("select p from Person p left join fetch p.events where
p.id = :pid")
.setParameter("pid", personId)
.uniqueResult(); // Eager fetch the collection so we can use it
detached
Event anEvent = (Event) session.load(Event.class, eventId);
session.getTransaction().commit();
// End of first unit of work
aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
// Begin second unit of work
Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
session2.beginTransaction();
session2.update(aPerson); // Reattachment of aPerson
session2.getTransaction().commit();
}
update の呼び出しは分離オブジェクトを再び永続化します。これは、新しい作業単位 (Unit of Work) に
バインドすると言えるでしょう。そのため分離の間に加えられたどのような修正もデータベースにセーブ
できます。エンティティオブジェクトのコレクションへの修正(追加·削除)も同様にセーブできます。
Well, this is not much use in our current situation, but it's an important concept you can design into your
own application. For now, complete this exercise by adding a new action to the EventManager's main
method and call it from the command line. If you need the identifiers of a person and an event - the
save() method returns it (you might have to modify some of the previous methods to return that
identifier):
else if (args[0].equals("addpersontoevent")) {
Long eventId = mgr.createAndStoreEvent("My Event", new Date());
Long personId = mgr.createAndStorePerson("Foo", "Bar");
mgr.addPersonToEvent(personId, eventId);
System.out.println("Added person " + personId + " to event " + eventId);
}
T his was an example of an association between two equally important classes, two entities. As
mentioned earlier, there are other classes and types in a typical model, usually "less important". Some
you have already seen, like an int or a String. We call these classes value types, and their instances
depend on a particular entity. Instances of these types don't have their own identity, nor are they shared
between entities (two persons don't reference the same firstnam e object, even if they have the same
first name). Of course, value types can not only be found in the JDK (in fact, in a Hibernate application all
JDK classes are considered value types), but you can also write dependent classes yourself, Address
or MonetaryAm ount, for example.
値型のコレクションを設計することもできます。これは他のエンティティへの参照のコレクションとは概
25
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
念的に非常に異なりますが、 Java ではほとんど同じように見えます。
1.3.4. 値のコレクション
値型オブジェクトのコレクションを Person エンティティへ追加します。 E メールアドレスを格納した
いのですが、 String 型を使っているので、コレクションは再び Set です:
private Set emailAddresses = new HashSet();
public Set getEmailAddresses() {
return emailAddresses;
}
public void setEmailAddresses(Set emailAddresses) {
this.emailAddresses = emailAddresses;
}
この Set のマッピングです:
<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
<key column="PERSON_ID"/>
<element type="string" column="EMAIL_ADDR"/>
</set>
T he difference compared with the earlier mapping is the elem ent part, which tells Hibernate that the
collection does not contain references to another entity, but a collection of elements of type String (the
lowercase name tells you it's a Hibernate mapping type/converter). Once again, the table attribute of
the set element determines the table name for the collection. T he key element defines the foreign-key
column name in the collection table. T he colum n attribute in the elem ent element defines the column
name where the String values will actually be stored.
更新したスキーマを見てください:
_____________
__________________
|
|
|
|
_____________
|
EVENTS
|
|
PERSON_EVENT
|
|
|
___________________
|_____________|
|__________________|
|
PERSON
|
|
|
|
|
|
|_____________|
PERSON_EMAIL_ADDR |
| *EVENT_ID
| <--> | *EVENT_ID
|
|
|
|___________________|
| EVENT_DATE |
| *PERSON_ID
| <--> | *PERSON_ID | <-->
*PERSON_ID
|
| TITLE
|
|__________________|
| AGE
|
*EMAIL_ADDR
|
|_____________|
| FIRSTNAME |
|___________________|
| LASTNAME
|
|_____________|
|
|
|
|
You can see that the primary key of the collection table is in fact a composite key, using both columns.
T his also implies that there can't be duplicate email addresses per person, which is exactly the
semantics we need for a set in Java.
26
第1章 Hibernate の導入
You can now try and add elements to this collection, just like we did before by linking persons and
events. It's the same code in Java:
private void addEmailToPerson(Long personId, String emailAddress) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Person aPerson = (Person) session.load(Person.class, personId);
// The getEmailAddresses() might trigger a lazy load of the collection
aPerson.getEmailAddresses().add(emailAddress);
session.getTransaction().commit();
}
T his time we didnt' use a fetch query to initialize the collection. Hence, the call to its getter method will
trigger an additional select to initialize it, so we can add an element to it. Monitor the SQL log and try to
optimize this with an eager fetch.
1.3.5. 双方向関連
Next we are going to map a bi-directional association - making the association between person and
event work from both sides in Java. Of course, the database schema doesn't change, we still have
many-to-many multiplicity. A relational database is more flexible than a network programming language,
so it doesn't need anything like a navigation direction - data can be viewed and retrieved in any possible
way.
まず Event イベントクラスに参加者のコレクションを追加します:
private Set participants = new HashSet();
public Set getParticipants() {
return participants;
}
public void setParticipants(Set participants) {
this.participants = participants;
}
それでは Event.hbm .xm l で関連のこちら側をマッピングしてください。
<set name="participants" table="PERSON_EVENT" inverse="true">
<key column="EVENT_ID"/>
<many-to-many column="PERSON_ID" class="events.Person"/>
</set>
As you see, these are normal set mappings in both mapping documents. Notice that the column names
in key and m any-to-m any are swapped in both mapping documents. T he most important addition here
is the inverse="true" attribute in the set element of the Event's collection mapping.
この指定の意味は、2つの間のエンティティ間のリンクについての情報を探す必要があるとき、 Hibernate
は反対側のエンティティ、つまり Person クラスから探すということです。一度2つのエンティティ間の
双方向リンクがどのように作成されるかがわかれば、これを理解することはとても簡単です。
1.3.6. 双方向リンクの動作
27
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
First, keep in mind that Hibernate does not affect normal Java semantics. How did we create a link
between a Person and an Event in the unidirectional example? We added an instance of Event to the
collection of event references, of an instance of Person. So, obviously, if we want to make this link
working bi-directional, we have to do the same on the other side - adding a Person reference to the
collection in an Event. T his "setting the link on both sides" is absolutely necessary and you should
never forget doing it.
多くの開発者は慎重にプログラムするので、エンティティの両側に正しく関連を設定するリンク管理メ
ソッドを作成します。例えば Person では以下のようになります。:
protected Set getEvents() {
return events;
}
protected void setEvents(Set events) {
this.events = events;
}
public void addToEvent(Event event) {
this.getEvents().add(event);
event.getParticipants().add(this);
}
public void removeFromEvent(Event event) {
this.getEvents().remove(event);
event.getParticipants().remove(this);
}
コレクションのゲットとセットメソッドが現在 protected になっていることに注意してください。これは
同じパッケージのクラスやサブクラスのメソッドは依然アクセスが可能ですが、 (ほとんど) そのパッ
ケージ外のどのクラスでも直接そのコレクションを台無しにすることを防ぎます。おそらく反対側のコレ
クションにも同じことをした方がいいでしょう。
What about the inverse mapping attribute? For you, and for Java, a bi-directional link is simply a matter
of setting the references on both sides correctly. Hibernate however doesn't have enough information to
correctly arrange SQL INSERT and UPDAT E statements (to avoid constraint violations), and needs
some help to handle bi-directional associations properly. Making one side of the association inverse
tells Hibernate to basically ignore it, to consider it a mirror of the other side. T hat's all that is necessary
for Hibernate to work out all of the issues when transformation a directional navigation model to a SQL
database schema. T he rules you have to remember are straightforward: All bi-directional associations
need one side as inverse. In a one-to-many association it has to be the many-side, in many-to-many
association you can pick either side, there is no difference.
Let's turn this into a small web application.
1.4. パ ー ト 3 - EventManager Web ア プ リ ケ ー シ ョ ン
Hibernate の Web アプリケーションは、スタンドアローンのアプリケーションのように Session と
T ransaction を使用します。しかしいくつかの一般的なパターンが役立ちます。ここで
EventManagerServlet を作成します。このサーブレットは、データベースに格納した全てのイベント
をリストにでき、さらに HT ML フォームから新しいイベントを入力できるものです。
1.4.1. 基本的な Servlet の記述
新しいクラスを、ソースディレクトリの events パッケージに作成してください。
28
第1章 Hibernate の導入
package events;
// Imports
public class EventManagerServlet extends HttpServlet {
// Servlet code
}
Servlet は HT T P の GET リクエストのみを処理するので、 doGet() を実装します。
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
try {
// Begin unit of work
HibernateUtil.getSessionFactory()
.getCurrentSession().beginTransaction();
// Process request and render page...
// End unit of work
HibernateUtil.getSessionFactory()
.getCurrentSession().getTransaction().commit();
} catch (Exception ex) {
HibernateUtil.getSessionFactory()
.getCurrentSession().getTransaction().rollback();
throw new ServletException(ex);
}
}
T he pattern we are applying here is called session-per-request. When a request hits the servlet, a new
Hibernate Session is opened through the first call to getCurrentSession() on the
SessionFactory. T hen a database transaction is started - all data access as to occur inside a
transaction, no matter if data is read or written (we don't use the auto-commit mode in applications).
全てのデータベースオペレーションで新しい Hibernate Session を使用 しないでください 。全てのリク
エストで機能する、1つの Hibernate Session を使用してください。自動的に現在の Java スレッドにバ
インドされるので、 getCurrentSession() を使用してください。
Next, the possible actions of the request are processed and the response HT ML is rendered. We'll get
to that part soon.
Finally, the unit of work ends when processing and rendering is complete. If any problem occured during
processing or rendering, an exception will be thrown and the database transaction rolled back. T his
completes the session-per-request pattern. Instead of the transaction demarcation code in every
servlet you could also write a servlet filter. See the Hibernate website and Wiki for more information
about this pattern, called Open Session in View - you'll need it as soon as you consider rendering your
view in JSP, not in a servlet.
1.4.2. 処理と描画
29
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
Let's implement the processing of the request and rendering of the page.
// Write HTML header
PrintWriter out = response.getWriter();
out.println("<html><head><title>Event Manager</title></head><body>");
// Handle actions
if ( "store".equals(request.getParameter("action")) ) {
String eventTitle = request.getParameter("eventTitle");
String eventDate = request.getParameter("eventDate");
if ( "".equals(eventTitle) || "".equals(eventDate) ) {
out.println("<b><i>Please enter event title and date.</i></b>");
} else {
createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
out.println("<b><i>Added event.</i></b>");
}
}
// Print page
printEventForm(out);
listEvents(out, dateFormatter);
// Write HTML footer
out.println("</body></html>");
out.flush();
out.close();
Java と HT ML が混在するコーディングスタイルは、より複雑なアプリケーションには適していないで
しょう (このチュートリアルでは、基本的な Hibernate のコンセプトを示しているだけであることを覚え
ておいてください)。このコードは HT ML のヘッダーとフッターの記述です。このページには、イベント
を入力する HT ML フォームと、データベースにある全てのイベントのリストが表示されます。最初のメ
ソッドはごく単純な HT ML 出力です。
private void printEventForm(PrintWriter out) {
out.println("<h2>Add new event:</h2>");
out.println("<form>");
out.println("Title: <input name='eventTitle' length='50'/><br/>");
out.println("Date (e.g. 24.12.2009): <input name='eventDate'
length='10'/><br/>");
out.println("<input type='submit' name='action' value='store'/>");
out.println("</form>");
}
listEvents() メソッドは、現在のスレッドに結びつく Hibernate の Session を使用して、クエリを実
行します。
30
第1章 Hibernate の導入
private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
List result = HibernateUtil.getSessionFactory()
.getCurrentSession().createCriteria(Event.class).list();
if (result.size() > 0) {
out.println("<h2>Events in database:</h2>");
out.println("<table border='1'>");
out.println("<tr>");
out.println("<th>Event title</th>");
out.println("<th>Event date</th>");
out.println("</tr>");
for (Iterator it = result.iterator(); it.hasNext();) {
Event event = (Event) it.next();
out.println("<tr>");
out.println("<td>" + event.getTitle() + "</td>");
out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
out.println("</tr>");
}
out.println("</table>");
}
}
最後に、 store アクションが createAndStoreEvent() メソッドを呼び出します。このメソッドでも
現在のスレッドの Session を利用します。
protected void createAndStoreEvent(String title, Date theDate) {
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
HibernateUtil.getSessionFactory()
.getCurrentSession().save(theEvent);
}
T hat's it, the servlet is complete. A request to the servlet will be processed in a single Session and
T ransaction. As earlier in the standalone application, Hibernate can automatically bind these ojects to
the current thread of execution. T his gives you the freedom to layer your code and access the
SessionFactory in any way you like. Usually you'd use a more sophisticated design and move the
data access code into data access objects (the DAO pattern). See the Hibernate Wiki for more
examples.
1.4.3. デプロイとテスト
このアプリケーションのデプロイのために、 Web アーカイブ(WAR)を作成してください。以下の Ant
ターゲットを build.xm l に加えてください。
<target name="war" depends="compile">
<war destfile="hibernate-tutorial.war" webxml="web.xml">
<lib dir="${librarydir}">
<exclude name="jsdk*.jar"/>
</lib>
<classes dir="${targetdir}"/>
</war>
</target>
このターゲットは hibernate-tutorial.war というファイルをプロジェクトディレクトリに作成しま
31
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
す。このファイルはすべてのライブラリと web.xm l 記述子を含んでおり、プロジェクトのベースディレ
クトリに置かれることを期待されます:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>Event Manager</servlet-name>
<servlet-class>events.EventManagerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Event Manager</servlet-name>
<url-pattern>/eventmanager</url-pattern>
</servlet-mapping>
</web-app>
Before you compile and deploy the web application, note that an additional library is required: jsdk.jar.
T his is the Java servlet development kit, if you don't have this library already, get it from the Sun website
and copy it to your library directory. However, it will be only used for compliation and excluded from the
WAR package.
T o build and deploy call ant war in your project directory and copy the hibernate-tutorial.war
file into your T omcat webapp directory. If you don't have T omcat installed, download it and follow the
installation instructions. You don't have to change any T omcat configuration to deploy this application
though.
一度デプロイして T omcat を起動すれば、 http://localhost:8080/hibernatetutorial/eventm anager でアプリケーションへのアクセスが可能です。最初のリクエストが作成した
サーブレットに渡ったときに、 T omcat のログで Hibernate の初期化処理を確認してください (
HibernateUtil 内の静的初期化ブロックが呼ばれています)。また、例外が発生したなら詳細を確認
してください。
1.5. 要 約
このチュートリアルでは、簡単なスタンドアローンの Hibernate アプリケーションと小規模の Web アプ
リケーションを書くための基本を紹介しました。
If you already feel confident with Hibernate, continue browsing through the reference documentation
table of contents for topics you find interesting - most asked are transactional processing (11章トランザ
クションと並行性), fetch performance (19章パフォーマンスの改善), or the usage of the API (10章オブ
ジェクトを扱う) and the query features (「クエリ」).
Don't forget to check the Hibernate website for more (specialized) tutorials.
32
第2章 アーキテクチャ
第 2章 アーキテクチャ
2.1. 概 観
Hibernate アーキテクチャの(非常に)高いレベルからのビュー:
図 2.1 High Level view of the Hibernate Architecture
この図は Hibernate が、アプリケーションに対して永続化サービス (と永続オブジェクト)を提供するた
めに、データベースと設定データを使うことを示しています。
We would like to show a more detailed view of the runtime architecture. Unfortunately, Hibernate is
flexible and supports several approaches. We will show the two extremes. T he "lite" architecture has the
application provide its own JDBC connections and manage its own transactions. T his approach uses a
minimal subset of Hibernate's APIs:
33
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
図 2.2 T he Lite Architecture
T he "full cream" architecture abstracts the application away from the underlying JDBC/JT A APIs and lets
Hibernate take care of the details.
以下は、図に含まれるオブジェクトの定義です:
SessionFactory (org.hibernate.SessionFactory)
1つのデータベースに対するコンパイルされたマッピングのスレッドセーフな(更新不能の)
キャッシュ。 Session のファクトリであり、 ConnectionProvider のクライアント。オプ
ションとして、プロセスまたはクラスタレベルにおいて、トランザクション間で再利用可能な
データの(二次)キャッシュを持ちます。
Session (org.hibernate.Session)
アプリケーションと永続ストアとの対話を表す、シングルスレッドで短命のオブジェクト。
JDBC コネクションをラップします。 T ransaction のファクトリです。永続オブジェクトの必
須の(一次)キャッシュを保持します。このキャッシュはオブジェクトグラフをナビゲーション
する時や、識別子でオブジェクトを検索する時に使われます。
Persistent objects と Collections
永続化状態とビジネス機能を持つ、短命でシングルスレッドのオブジェクト。これは通常の
JavaBeans/POJO のこともありますが、特徴的なことは、その時点での(ただ1つの)
Session と関連していることです。 Session がクローズされるとすぐに、それらは切り離さ
れて他のアプリケーション層から自由に使うことができます(例えばデータトランスファオブ
ジェクトとして、プレゼンテーション層から、またはプレゼンテーション層へ直接使用できま
す)。
T ransient と detached な objects と Collections
現時点では Session と関連していない、永続クラスのインスタンス。すでにアプリケーション
側でインスタンス化されていて、まだ永続化されていないか、クローズされた Session でイン
スタンス化されたかのどちらかです。
34
第2章 アーキテクチャ
T ransaction (org.hibernate.T ransaction)
(オプション) 原子性を持つ作業単位 (Unit of Work) を指定するために、アプリケーションが使用
する、シングルスレッドで短命なオブジェクト。下に位置する JDBC 、 JT A 、 CORBA トラン
ザクションからアプリケーションを抽象化します。 Session は、時にはいくつかの
T ransaction をまたがるかもしれません。しかし、下の層の API を使うにせよ、
T ransaction を使うにせよ、トランザクション境界を設定することは、決してオプションでは
ありません。
ConnectionProvider (org.hibernate.connection.ConnectionProvider)
(オプション) JDBC コネクション(とそのプール)のファクトリ。下の層に位置する
Datasource や DriverManager からアプリケーションを抽象化します。アプリケーションに
は公開されませんが、開発者が継承または実装することは可能です。
T ransactionFactory (org.hibernate.T ransactionFactory)
(オプション) T ransaction インスタンスのファクトリ。アプリケーションには公開されません
が、開発者が継承または実装することは可能です。
Extension Interfaces
Hibernate は、永続層の振る舞いをカスタマイズするために、多くのオプション拡張インタ
フェースを用意しています。詳細は API ドキュメントを参照してください。
Given a "lite" architecture, the application bypasses the T ransaction/T ransactionFactory and/or
ConnectionProvider APIs to talk to JT A or JDBC directly.
2.2. イ ン ス タ ン ス の 状 態
永続クラスのインスタンスは、次の3つの異なる状態のどれかになります。それは、 永続コンテキスト
によって決まります。 Hibernate の Session オブジェクトが、永続コンテキストになります:
transient
この状態のインスタンスは、現在もそして過去においても、永続コンテキストに関連づいていま
せん。また、永続 ID (主キーの値)を 持っていません。
persistent
この状態のインスタンスは、その時点で永続コンテキストに関連づいています。また、永続 ID
(主キーの値)を持ち、たいていはデータベースに対応する行を持っているでしょう。特定の永
続コンテキストのなかでは、永続 ID が Java の ID (オブジェクトのメモリ上の位置)と同じで
あることを Hibernate が 保証 します。
detached
この状態のインスタンスは、かつて永続コンテキストに関連づけられたが、そのコンテキストが
クローズされたか、あるいは、他のプロセスにそのインスタンスがシリアライズされたかです。
このインスタンスは、永続 ID を持ち、たいていはデータベースに対応する行を持っているで
しょう。分離インスタンスに対しては、永続 ID と Java の ID との関連は、 Hibernate が保証し
ません。
35
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
2.3. JMX と の 統 合
JMX は Java コンポーネント管理の J2EE 標準です。 JMX 標準サービスを通して、 Hibernate は管理され
ます。ディストリビューションの中に org.hibernate.jm x.HibernateService という MBean 実
装を用意しています。
JBoss アプリケーションサーバー上に Hibernate を JMX サービスとしてデプロイする方法の例として
は、 JBoss ユーザーガイドを参照してください。 JBoss アプリケーションサーバーにおいて、 JMX を
使ってデプロイすると、次のメリットが得られます:
Session Management: T he Hibernate Session's lifecycle can be automatically bound to the scope
of a JT A transaction. T his means you no longer have to manually open and close the Session, this
becomes the job of a JBoss EJB interceptor. You also don't have to worry about transaction
demarcation in your code anymore (unless you'd like to write a portable persistence layer of course,
use the optional Hibernate T ransaction API for this). You call the HibernateContext to access
a Session.
HAR デプロイ: 通常、( EAR または SAR ファイルにある) JBoss サービスデプロイメントディスクリ
プタを使って、 Hibernate JMX サービスをデプロイします。それは、 Hibernate の
SessionFactory の全ての一般的な設定オプションをサポートします。しかし依然としてデプロイ
メントディスクリプタのなかにすべてのマッピングファイルの名前を挙げる必要があります。もし、
オプションの HAR デプロイメントを使うことを決めたなら、 JBoss は自動的に HAR ファイルのなか
の全てのマッピングファイルを検出します。
これらのオプションについての詳細な情報は、 JBoss アプリケーションサーバーユーザーガイドを参考に
してください。
Another feature available as a JMX service are runtime Hibernate statistics. See 「Hibernate 統計」.
2.4. JCA サ ポ ー ト
Hibernate は JCA コネクタとしても設定できます。詳細については、 Web サイトを見てください。
Hibernate JCA サポートは、今のところ実験段階として考えられていることに注意してください。
2.5. コ ン テ キ ス ト 上 の セ ッ シ ョ ン
Most applications using Hibernate need some form of "contextual" sessions, where a given session is in
effect throughout the scope of a given context. However, across applications the definition of what
constitutes a context is typically different; and different contexts define different scopes to the notion of
current. Applications using Hibernate prior to version 3.0 tended to utilize either home-grown
T hreadLocal-based contextual sessions, helper classes such as HibernateUtil, or utilized thirdparty frameworks (such as Spring or Pico) which provided proxy/interception-based contextual sessions.
バージョン 3.0.1 から、 Hibernate には SessionFactory.getCurrentSession() メソッドが加わり
ました。これは、 JT A トランザクションの使用を前提にしています。 JT A トランザクションは、現在の
セッションのスコープとコンテキストの両方を定義します。 Hibernate チームは、次のことを主張しま
す。巨大なスタンドアロンの JT A T ransactionManager 実装が成熟したら、 J2EE コンテナ上にデ
プロイされるかどうかにかかわらず、ほとんどの(すべてとは言わないが)アプリケーションが、 JT A ト
ランザクション管理を使用すべきであると。この考えに基づくと、 JT A ベースの「コンテキスト上のセッ
ション」を使うしかないでしょう。
しかし、バージョン 3.1 からは、 SessionFactory.getCurrentSession() の後の処理が、プラガ
ブルになりました。これを受けて、現在のセッションを定義するスコープとコンテキストのプラガビリ
36
第2章 アーキテクチャ
ティを可能にするために、新しい拡張インタフェース (
org.hibernate.context.CurrentSessionContext ) と新しい構成パラメータ (
hibernate.current_session_context_class ) が追加されました。
org.hibernate.context.CurrentSessionContext インタフェースの規約についての詳細な内容
は Javadoc を参照してください。それには、 currentSession() という1つのメソッドが定義されてお
り、その実装は、現在の「コンテキスト上のセッション」を追跡することに責任を持ちます。そのまま使
えるように、 Hibernate はこのインタフェースの実装を2つ提供しています。
org.hibernate.context.JT ASessionContext - JT A トランザクションによって、現在のセッ
ションが追跡され、スコープを決められます。この処理は、古い JT A だけのアプローチとまったく同
じです。詳細は Javadoc を参照してください。
org.hibernate.context.T hreadLocalSessionContext - スレッドの実行によって、現在の
セッションが追跡されます。詳細は Javadoc を参照してください。
org.hibernate.context.ManagedSessionContext - スレッドの実行によって、現在のセッ
ションが追跡されます。しかし、このクラスの static メソッドで Session インスタンスをバインド/
アンバインドする責任はあなたにあります。これは決して Session をオープン、フラッシュ、ク
ローズしません。
T he first two implementations provide a "one session - one database transaction" programming model,
also known and used as session-per-request. T he beginning and end of a Hibernate session is defined
by the duration of a database transaction. If you use programatic transaction demarcation in plain JSE
without JT A, you are adviced to use the Hibernate T ransaction API to hide the underlying transaction
system from your code. If you use JT A, use the JT A interfaces to demarcate transactions. If you execute
in an EJB container that supports CMT , transaction boundaries are defined declaratively and you don't
need any transaction or session demarcation operations in your code. Refer to 11章トランザクションと
並行性 for more information and code examples.
T he hibernate.current_session_context_class configuration parameter defines which
org.hibernate.context.CurrentSessionContext implementation should be used. Note that for
backwards compatibility, if this config param is not set but a
org.hibernate.transaction.T ransactionManagerLookup is configured, Hibernate will use
the org.hibernate.context.JT ASessionContext. T ypically, the value of this parameter would
just name the implementation class to use; for the three out-of-the-box implementations, however, there
are two corresponding short names, "jta", "thread", and "managed".
37
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 3章 設定
Hibernate は様々な環境で動作するようにデザインされているため、非常に多くの設定要素があります。
幸いなことに、 Hibernate は、公開されているパッケージの etc/ フォルダの
hibernate.properties に、ほとんどの設定要素の適切なデフォルト値が記述されています。この
hibernate.properties をクラスパスに設定し、設定要素をカスタマイズするだけです。
3.1. プ ロ グ ラ ム 上 の 設 定
An instance of org.hibernate.cfg.Configuration represents an entire set of mappings of an
application's Java types to an SQL database. T he Configuration is used to build an (immutable)
SessionFactory. T he mappings are compiled from various XML mapping files.
通常、 Configuration インスタンスは、特定の XML マッピングファイルによって直接初期化されま
す。もし、マッピングファイルがクラスパスに設定されている場合、次のメソッドを使ってください。
addResource() :
Configuration cfg = new Configuration()
.addResource("Item.hbm.xml")
.addResource("Bid.hbm.xml");
代替案 (こちらのほうが良いときもあります) としてマッピングクラスを指定する方法もあります。
Hibernate に、マッピングファイルを 見つけさせてください:
Configuration cfg = new Configuration()
.addClass(org.hibernate.auction.Item.class)
.addClass(org.hibernate.auction.Bid.class);
Hibernate は、クラスパスにある以下のような名前のマッピングファイルを見つけます。
/org/hibernate/auction/Item .hbm .xm l 、 /org/hibernate/auction/Bid.hbm .xm l 。こ
の方法だと、ハードコーディングされたファイル名を排除できます。
Configuration は、設定プロパティを指定することもできます:
Configuration cfg = new Configuration()
.addClass(org.hibernate.auction.Item.class)
.addClass(org.hibernate.auction.Bid.class)
.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
.setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
.setProperty("hibernate.order_updates", "true");
Hibernate に設定プロパティを渡す方法は1つではありません。さまざまなオプションを用意しています:
1. java.util.Properties インスタンスを Configuration.setProperties() に渡します。
2. hibernate.properties をクラスパスのルートディレクトリに置きます。
3. System プロパティが java -Dproperty=value を使うように設定します。
4. Include <property> elements in hibernate.cfg.xm l (discussed later).
今すぐ始めたいのなら、 hibernate.properties を使うのが一番の近道です。
Configuration は、起動時にだけあるオブジェクトであり、一度 SessionFactory を生成した後
は、破棄されることを意図しています。
38
第3章 設定
3.2. SessionFactory を 取 得 す る
Configuration がすべてのマッピング情報を解析したら、アプリケーションは、 Session インスタン
スのためにファクトリを取得しなければなりません。この SessionFactory は、 Hibernate を使用するす
べてのスレッドで共有されるべきです:
SessionFactory sessions = cfg.buildSessionFactory();
Hibernate は、アプリケーションが SessionFactory を複数生成することを可能にします。これは、複
数のデータベースを使用する場合に便利です。
3.3. JDBC コ ネ ク シ ョ ン
通常、開発者は SessionFactory を生成し、 SessionFactory で JDBC コネクションをプーリングした
いと考えます。そのアプローチを採用する場合、単純に Session をオープンしてください:
Session session = sessions.openSession(); // open a new Session
これだけで、プーリングした JDBC コネクションを使って目的のデータベースにアクセスすることができ
ます。
そのためには、 JDBC コネクションのプロパティを Hibernate に設定する必要があります。すべての
Hibernate プロパティ名とセマンティクスは org.hibernate.cfg.Environm ent クラスに定義され
ています。この設定は JDBC コネクション設定の中で一番重要なものです。
もし、以下のプロパティを設定すると、 Hibernate はコネクションを取得するために(プールも)
java.sql.DriverManager を使います:
表 3.1 Hibernate JDBC プロパティ
プロパティ名
意味
hibernate.connection.driver_class
jdbc driver class
hibernate.connection.url
jdbc URL
hibernate.connection.usernam e
database user
hibernate.connection.password
database user password
hibernate.connection.pool_size
maximum number of pooled connections
Hibernate's own connection pooling algorithm is however quite rudimentary. It is intended to help you get
started and is not intended for use in a production system or even for performance testing. You should
use a third party pool for best performance and stability. Just replace the
hibernate.connection.pool_size property with connection pool specific settings. T his will turn
off Hibernate's internal pool. For example, you might like to use C3P0.
C3P0 is an open source JDBC connection pool distributed along with Hibernate in the lib directory.
Hibernate will use its C3P0ConnectionProvider for connection pooling if you set
hibernate.c3p0.* properties. If you'd like to use Proxool refer to the packaged
hibernate.properties and the Hibernate web site for more information.
C3P0 用の hibernate.properties ファイルを例として示します:
39
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
hibernate.connection.driver_class = org.postgresql.Driver
hibernate.connection.url = jdbc:postgresql://localhost/mydatabase
hibernate.connection.username = myuser
hibernate.connection.password = secret
hibernate.c3p0.min_size=5
hibernate.c3p0.max_size=20
hibernate.c3p0.timeout=1800
hibernate.c3p0.max_statements=50
hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
For use inside an application server, you should almost always configure Hibernate to obtain
connections from an application server Datasource registered in JNDI. You'll need to set at least one
of the following properties:
表 3.2 Hibernate データソースプロパティ
プロパティ名
意味
hibernate.connection.datasource
datasource JNDI name
hibernate.jndi.url
JNDI プロバイダの URL (オプション)
hibernate.jndi.class
JNDI のクラス InitialContextFactory (オプ
ション)
hibernate.connection.usernam e
データベースユーザ (オプション)
hibernate.connection.password
データベースユーザのパスワード (オプション)
Here's an example hibernate.properties file for an application server provided JNDI datasource:
hibernate.connection.datasource = java:/comp/env/jdbc/test
hibernate.transaction.factory_class = \
org.hibernate.transaction.JTATransactionFactory
hibernate.transaction.manager_lookup_class = \
org.hibernate.transaction.JBossTransactionManagerLookup
hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
JNDI データソースから取得した JDBC コネクションは、アプリケーションサーバーのコンテナ管理トラ
ンザクションに自動的に参加します。
Arbitrary connection properties may be given by prepending "hibernate.connnection" to the
property name. For example, you may specify a charSet using hibernate.connection.charSet.
JDBC コネクションを取得する戦略を持つ独自のプラグインを定義する場合は、
org.hibernate.connection.ConnectionProvider インターフェースを実装してください。そし
て、実装クラスを hibernate.connection.provider_class に設定してください。
3.4. オ プ シ ョ ン 設 定 プ ロ パ テ ィ
これらのプロパティはランタイムに Hibernate の挙動を制御するものです。これらのプロパティはすべて
妥当なデフォルト値があり、任意で設定します。
Warning: some of these properties are "system-level" only. System-level properties can be set only via
java -Dproperty=value or hibernate.properties. T hey may not be set by the other
techniques described above.
40
第3章 設定
表 3.3 Hibernate 設定プロパティ
プロパティ名
意味
hibernate.dialect
Hibernate のクラス名 Dialect が入ります。こ
れはリレーショナルデータベースごとに最適化さ
れた SQL を生成します。
例 full.classnam e.of.Dialect
hibernate.show_sql
発行されたすべての SQL をコンソールに出力しま
す。これはログカテゴリの
org.hibernate.SQL に debug を設定する方法
の代替手段です。
true | false
hibernate.form at_sql
ログとコンソールの SQL を整形して表示します。
true | false
hibernate.default_schem a
生成される SQL 文のテーブルに設定するスキー
マ/テーブルスペースです。 例 .SCHEMA_NAME
hibernate.default_catalog
生成される SQL 文のテーブルに設定するカタログ
です。 例 CAT ALOG_NAME
hibernate.session_factory_nam e
SessionFactory は生成後、この名前で JNDI
に自動的に登録されます。
例 jndi/com posite/nam e
hibernate.m ax_fetch_depth
Set a maximum "depth" for the outer join fetch
tree for single-ended associations (one-to-one,
many-to-one). A 0 disables default outer join
fetching.
例: 推奨する値は 0 から 3 の間です。
hibernate.default_batch_fetch_size
関連フェッチのデフォルト Hibernate バッチサイ
ズを指定します。 例: 推奨する値は 4 , 8 , 16 で
す。
hibernate.default_entity_m ode
SessionFactory からセッションをオープンし
たときに使用するエンティティのデフォルトモー
ドを設定します。
dynam ic-m ap, dom 4 j, pojo
hibernate.order_updates
項目が更新されたときに、別の SQL で主キーを更
新することを強制します。この場合、同時実行可
能なシステムでは、まれにデッドロックが発生す
る可能性があります。 true | false
hibernate.generate_statistics
有効の場合、 Hibernate はパフォーマンスチュー
ニングに有効な統計情報を収集します。 true |
false
hibernate.use_identifer_rollback
有効の場合、オブジェクトが削除されたときに識
別子プロパティをリセットし、デフォルト値にし
たものを生成します。 true | false
hibernate.use_sql_com m ents
有効の場合、 SQL 内にコメントを生成します。こ
41
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
れはデバックを容易にします。デフォルトの値は
false です。
true | false
42
第3章 設定
表 3.4 Hibernate JDBC とコネクションプロパティ
プロパティ名
意味
hibernate.jdbc.fetch_size
値が0でない場合、 JDBC フェッチサイズを決定
します ( Statem ent.setFetchSize() を呼び
ます)。
hibernate.jdbc.batch_size
値が0でない場合、 Hibernate が JDBC2 バッチ更
新を使用します。 例: 推奨する値は 5 から 30
の間です。
hibernate.jdbc.batch_versioned_data
もし JDBC ドライバが executeBatch() によっ
て正確な行数を返す場合、このプロパティを
true にしてください (通常はこのオプションを
ON するのが安全です)。 Hibernate は、自動
バージョンデータのためバッチ DML を使います。
デフォルトの値は false です。
true | false
hibernate.jdbc.factory_class
カスタム Batcher を選びます。ほとんどのアプ
リケーションに、この設定プロパティは必要あり
ません。
例 classnam e.of.Batcher
hibernate.jdbc.use_scrollable_result
set
Hibernate による JDBC2 のスクロール可能なリザ
ルトセットの使用を有効にします。このプロパ
ティは、ユーザーによって提供された JDBC コネ
クションを使用している場合のみ必要で、そうで
なければ Hibernate はコネクションメタデータを
使用します。 true | false
hibernate.jdbc.use_stream s_for_binar
y
JDBC へ/から binary や serializable の書き
込み/読み込みストリームを使います (システムレ
ベルのプロパティ)。
true | false
hibernate.jdbc.use_get_generated_key
s
挿入の後に自動生成された主キーを取得するため
の JDBC3
PreparedStatem ent.getGeneratedKeys()
の使用を有効にします。これは JDBC3+ ドライバ
と JRE1.4+ を必要とし、もし Hibernate の識別子
ジェネレータに問題が発生するようなら false に
設定してください。デフォルトではコネクション
メタデータを使いドライバの能力を決定します。
例 true|false
hibernate.connection.provider_class
JDBC コネクションを Hibernate に提供する独自
の ConnectionProvider のクラス名。
例 classnam e.of.ConnectionProvider
hibernate.connection.isolation
JDBC トランザクション分離レベルを設定しま
す。妥当な値を調べるためには
java.sql.Connection をチェックしてくださ
43
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
い。しかし使用するデータベースが、すべての分
離レベルをサポートしているとは限りません。
例 1, 2, 4 , 8
hibernate.connection.autocom m it
プールされている JDBC コネクションの自動コ
ミットを有効にする(非推奨)。 true | false
hibernate.connection.release_m ode
Hibernate がいつ JDBC コネクションをリリース
するかを指定します。デフォルトではセッション
が明示的にクローズまたは切断されてもコネク
ションは保持します。アプリケーションサーバー
の JT A データソースの場合、すべての JDBC コー
ルの後、強制的にコネクションをリリースするた
めに after_statem ent を使ってください。非
JT A コネクションの場合、各トランザクションが
終了したときに after_transaction を使い、
コネクションをリリースしてください。 auto に
すると、 JT A や CMT トランザクションの場合、
after_statem ent でクローズし、 JDBC トラ
ンザクションの場合、 after_transaction で
クローズします。
例 auto (デフォルト) | on_close |
after_transaction | after_statem ent
Note that this setting only affects Session s
returned from
SessionFactory.openSession. For
Session s obtained through
SessionFactory.getCurrentSession, the
CurrentSessionContext implementation
configured for use controls the connection
release mode for those Session s. See 「コンテ
キスト上のセッション」.
hibernate.connection.<propertyNam e>
JDBC プロパティ propertyNam e を
DriverManager.getConnection() に渡しま
す。
hibernate.jndi.<propertyNam e>
プロパティ propertyNam e を JNDI
InitialContextFactory に渡します。
44
第3章 設定
表 3.5 Hibernate キャッシュプロパティ
プロパティ名
意味
hibernate.cache.provider_class
カスタム CacheProvider のクラス名です。
例 classnam e.of.CacheProvider
hibernate.cache.use_m inim al_puts
書き込みを最小限にするために、二次キャッシュ
の操作を最適化します。その代わりに、読み込み
がより頻繁に発生するようになります。このセッ
ティングはクラスタキャッシュで役に立ちます。
Hibernate3 ではクラスタキャッシュ実装用にデ
フォルトでは有効になっています。
例 true|false
hibernate.cache.use_query_cache
特定のクエリがキャッシュ可能な場合に、クエリ
キャッシュを有効にします。 例 true|false
hibernate.cache.use_second_level_cac
he
May be used to completely disable the second
level cache, which is enabled by default for
classes which specify a <cache> mapping.
例 true|false
hibernate.cache.query_cache_factory
カスタム QueryCache インターフェースのクラ
ス名を指定します。デフォルトでは
StandardQueryCache になります。
例 classnam e.of.QueryCache
hibernate.cache.region_prefix
二次キャッシュの領域名の接頭辞です。 prefix
hibernate.cache.use_structured_entri
es
二次キャッシュに格納するデータを、人が理解し
やすいフォーマットにします。 例 true|false
45
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
表 3.6 Hibernate トランザクションプロパティ
プロパティ名
意味
hibernate.transaction.factory_class
Hibernate T ransaction API と一緒に使われる
T ransactionFactory のクラス名です。 (デ
フォルトでは JDBCT ransactionFactory で
す)。
例 classnam e.of.T ransactionFactory>
jta.UserT ransaction
アプリケーションサーバーから JT A
UserT ransaction を取得するために
JT AT ransactionFactory に使われる JNDI 名
です。
例 jndi/com posite/nam e
hibernate.transaction.m anager_lookup
_class
T ransactionManagerLookup のクラス名で
す。 JT A 環境において、 JVM レベルのキャッ
シュを有効にするときか、 hilo ジェネレータが使
用されるときに必要です。
例 classnam e.of.T ransactionManagerLoo
kup
hibernate.transaction.flush_before_c
om pletion
If enabled, the session will be automatically
flushed during the before completion phase of the
transaction. Built-in and automatic session
context management is preferred, see 「コンテキ
スト上のセッション」.
true | false
hibernate.transaction.auto_close_ses
sion
If enabled, the session will be automatically
closed during the after completion phase of the
transaction. Built-in and utomatic session context
management is preferred, see 「コンテキスト上
のセッション」.
true | false
46
第3章 設定
表 3.7 その他のプロパティ
プロパティ名
意味
hibernate.current_session_context_cl
ass
Supply a (custom) strategy for the scoping of the
"current" Session. See 「コンテキスト上のセッ
ション」 for more information about the built-in
strategies.
例 jta | thread | m anaged | custom .Class
hibernate.query.factory_class
HQL パーサーの実装を選択します。
例 org.hibernate.hql.ast.AST QueryT ran
slatorFactory or
org.hibernate.hql.classic.ClassicQue
ryT ranslatorFactory
hibernate.query.substitutions
HQL と SQL のトークンをマッピングします。
(例えば、トークンは関数やリテラル名です)。
例 hqlLiteral=SQL_LIT ERAL,
hqlFunction=SQLFUNC
hibernate.hbm 2ddl.auto
SessionFactory を生成したときに、自動的に
スキーマ DDL を有効にしデータベースに出力しま
す。 create-drop の場合、 SessionFactory
をクローズしたときに、データベーススキーマを
ドロップします。
例 validate | update | create | createdrop
hibernate.cglib.use_reflection_optim
izer
実行時リフレクションの代わりの CGLIB の使用を
有効にします (システムレベルのプロパティ) 。
リフレクションはトラブルシューティングのとき
に役立つことがあります。オプティマイザをオフ
にしているときでさえ、 Hibernate には必ず
CGLIB が必要なことに注意してください。このプ
ロパティは hibernate.cfg.xm l で設定できま
せん。
true | false
3.4.1. SQL 方言( Dialect)
hibernate.dialect プロパティには、使用するデータベースの正しい
org.hibernate.dialect.Dialect のサブクラスを、必ず指定すべきです。しかし方言を指定すれ
ば、 Hibernate は上述したプロパティのいくつかについて、より適切なデフォルト値を使います。そうす
れば、それらを手作業で設定する手間が省けます。
47
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
表 3.8 Hibernate SQL Dialects (hibernate.dialect)
RDBMS
Dialect
DB2
org.hibernate.dialect.DB2Dialect
DB2 AS/400
org.hibernate.dialect.DB24 00Dialect
DB2 OS390
org.hibernate.dialect.DB2390Dialect
PostgreSQL
org.hibernate.dialect.PostgreSQLDial
ect
MySQL
org.hibernate.dialect.MySQLDialect
MySQL with InnoDB
org.hibernate.dialect.MySQLInnoDBDia
lect
MySQL with MyISAM
org.hibernate.dialect.MySQLMyISAMDia
lect
Oracle (any version)
org.hibernate.dialect.OracleDialect
Oracle 9i
org.hibernate.dialect.Oracle9iDiale
ct
Oracle 10g
org.hibernate.dialect.Oracle10gDial
ect
Sybase
org.hibernate.dialect.SybaseDialect
Sybase Anywhere
org.hibernate.dialect.SybaseAnywhere
Dialect
Microsoft SQL Server
org.hibernate.dialect.SQLServerDiale
ct
SAP DB
org.hibernate.dialect.SAPDBDialect
Informix
org.hibernate.dialect.Inform ixDiale
ct
HypersonicSQL
org.hibernate.dialect.HSQLDialect
Ingres
org.hibernate.dialect.IngresDialect
Progress
org.hibernate.dialect.ProgressDialec
t
Mckoi SQL
org.hibernate.dialect.MckoiDialect
Interbase
org.hibernate.dialect.InterbaseDiale
ct
Pointbase
org.hibernate.dialect.PointbaseDiale
ct
FrontBase
org.hibernate.dialect.FrontbaseDiale
ct
Firebird
org.hibernate.dialect.FirebirdDiale
ct
3.4.2. 外部結合フェッチ
もしデータベースが ANSI か、 Oracle か Sybase スタイルの外部結合をサポートしている場合、 outer
join fetching は、データベースの SQL 発行回数を節約しパフォーマンスを良くします(データベース内で
より多くの処理コストが発生します)。外部結合フェッチは、多対一、一対多、多対多、一対一のオブ
48
第3章 設定
ジェクト関連でグループオブジェクトを1つの SQL で SELECT します。
Outer join fetching may be disabled globally by setting the property hibernate.m ax_fetch_depth to
0. A setting of 1 or higher enables outer join fetching for one-to-one and many-to-one associations
which have been mapped with fetch="join".
See 「フェッチ戦略」 for more information.
3.4.3. バイナリストリーム
Oracle は JDBC ドライバとの間でやりとりされる byte 配列のサイズを制限します。 binary や
serializable 型の大きなインスタンスを使いたければ、
hibernate.jdbc.use_stream s_for_binary を有効にしてください。 ただし これはシステムレベ
ルの設定だけです 。
3.4.4. ニ次キャッシュとクエリキャッシュ
T he properties prefixed by hibernate.cache allow you to use a process or cluster scoped secondlevel cache system with Hibernate. See the 「第2レベルキャッシュ」 for more details.
3.4.5. クエリ言語の置き換え
hibernate.query.substitutions を使うことで、新しい Hibernate クエリトークンを定義できま
す。例:
hibernate.query.substitutions true=1, false=0
これはトークン true と false を、生成される SQL において整数リテラルに翻訳します。
hibernate.query.substitutions toLowercase=LOWER
これは SQL の LOWER 関数の名前の付け替えを可能にします。
3.4.6. Hibernate 統計
hibernate.generate_statistics を有効にした場合、動作しているシステムをチューニングすると
きに、 SessionFactory.getStatistics() を経由して、 Hibernate は便利な統計情報を出力しま
す。 JMX を経由して統計情報を出力することも可能です。 Javadoc の org.hibernate.stats パッ
ケージ内のインターフェースにはより多くの情報があります。
3.5. ロ ギ ン グ
Hibernate は Apache commons-logging を使って、さまざまなイベントをログとして出力します。
commons-logging サービスは(クラスパスに log4 j.jar を含めれば) Apache Log4j に、または
(JDK1.4 かそれ以上で実行させれば) JDK1.4 logging に直接出力します。 Log4j は
http://jakarta.apache.org からダウンロードできます。 Log4j を使うためには、クラスパスに
log4 j.properties ファイルを配置する必要があります。例のプロパティファイルは Hibernate と一緒
に配布され、それは src/ ディレクトリにあります。
We strongly recommend that you familiarize yourself with Hibernate's log messages. A lot of work has
been put into making the Hibernate log as detailed as possible, without making it unreadable. It is an
essential troubleshooting device. T he most interesting log categories are the following:
49
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
表 3.9 Hibernate ログカテゴリ
カテゴリ
機能
org.hibernate.SQL
実行したすべての SQL(DDL)ステートメントを
ロギングします。
org.hibernate.type
すべての JDBC パラメータをロギングします。
org.hibernate.tool.hbm 2ddl
実行したすべての SQL(DDL)ステートメントを
ロギングします。
org.hibernate.pretty
session に関連するすべてのエンティティ(最大
20)のフラッシュ時間をロギングします。
org.hibernate.cache
すべてのニ次キャッシュの動作をロギングしま
す。
org.hibernate.transaction
トランザクションに関連する動作をロギングしま
す。
org.hibernate.jdbc
JDBC リソース取得をロギングします。
org.hibernate.hql.ast.AST
HQL と SQL の AST のクエリパースをロギングし
ます。
org.hibernate.secure
すべての JAAS 分析をロギングします。
org.hibernate
すべてをロギングします。(情報が大量になりま
すが、トラブルシューティングには便利です)
Hibernate でアプリケーションを作成するときは、 org.hibernate.SQL カテゴリの debug を常に有
効にしておいたほうが良いでしょう。代替方法として、 hibernate.show_sql プロパティを有効にす
る方法があります。
3.6. NamingStrategy の 実 装
T he interface org.hibernate.cfg.Nam ingStrategy allows you to specify a "naming standard" for
database objects and schema elements.
You may provide rules for automatically generating database identifiers from Java identifiers or for
processing "logical" column and table names given in the mapping file into "physical" table and column
names. T his feature helps reduce the verbosity of the mapping document, eliminating repetitive noise
(T BL_ prefixes, for example). T he default strategy used by Hibernate is quite minimal.
マッピングを追加する前に Configuration.setNam ingStrategy() を呼ぶことで以下のように異な
る戦略を指定することができます:
SessionFactory sf = new Configuration()
.setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
.addFile("Item.hbm.xml")
.addFile("Bid.hbm.xml")
.buildSessionFactory();
org.hibernate.cfg.Im provedNam ingStrategy は組み込みの戦略です。これはいくつかのアプ
リケーションにとって有用な開始点となるかもしれません。
3.7. XML 設 定 フ ァ イ ル
もう1つの方法は hibernate.cfg.xm l という名前のファイルで十分な設定を指定する方法です。この
50
第3章 設定
ファイルは hibernate.properties ファイルの代わりとなります。もし両方のファイルがあれば、プ
ロパティが置き換えられます。
XML 設定ファイルは初期設定で CLASSPAT H の root に配置してください。これが例です:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!-- a SessionFactory instance listed as /jndi/name -->
<session-factory
name="java:hibernate/SessionFactory">
<!-- properties -->
<property name="connection.datasource">java:/comp/env/jdbc/MyDB</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">false</property>
<property name="transaction.factory_class">
org.hibernate.transaction.JTATransactionFactory
</property>
<property name="jta.UserTransaction">java:comp/UserTransaction</property>
<!-- mapping files -->
<mapping resource="org/hibernate/auction/Item.hbm.xml"/>
<mapping resource="org/hibernate/auction/Bid.hbm.xml"/>
<!-- cache settings -->
<class-cache class="org.hibernate.auction.Item" usage="read-write"/>
<class-cache class="org.hibernate.auction.Bid" usage="read-only"/>
<collection-cache collection="org.hibernate.auction.Item.bids"
usage="read-write"/>
</session-factory>
</hibernate-configuration>
見てのとおり、この方法の優位性は設定のためのマッピングファイル名を外出しにできることです。
Hibernate キャッシュをチューニングしなければならないのであれば、 hibernate.cfg.xm l はより便
利です。 hibernate.properties と hibernate.cfg.xm l の どちらかを使えることを覚えておい
てください。2つは同じもので、違うところといえば XML 構文を使うことの利点だけです。
XML 設定を使うことで、 Hibernate は以下のようにシンプルになります。
SessionFactory sf = new Configuration().configure().buildSessionFactory();
違う XML 設定ファイルを使うこともできます。
SessionFactory sf = new Configuration()
.configure("catdb.cfg.xml")
.buildSessionFactory();
3.8. J2EE ア プ リ ケ ー シ ョ ン サ ー バ ー と の 統 合
51
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
Hibernate は J2EE 構造と統合するポイントをサポートしています:
コンテナ管理データソース: Hibernate は JNDI が提供し、コンテナが管理する JDBC コネクションを
使用できます。通常、 JT A 準拠の T ransactionManager と ResourceManager がトランザク
ション管理 (CMT )、特に様々なデータソースにまたがる分散トランザクションを扱います。当然プロ
グラムでトランザクション境界を指定できます (BMT )。あるいは、記述したコードのポータビリティ
を保つために、オプションの Hibernate の T ransaction API を使いたくなるかもしれません。
自動 JNDI バインディング: Hibernate は JNDI が立ち上がった後に SessionFactory を生成しま
す。
JTA セッションバインディング: Hibernate Session は自動的に JT A トランザクションのスコープ
にバインドされます。単純に SessionFactory を JNDI から lookup して、現在の Session を取得
します。 JT A トランザクションが完了したときに、 Hibernateが Session をフラッシュし、クロー
ズします。トランザクション境界は、宣言 (CMT ) することも、プログラム (BMT /UserT ransaction)
することも可能です。
JMX デプロイメント: もし JMX が使用可能なアプリケーションサーバー(例えば JBoss AS) がある
場合、 Hibernate を MBean としてデプロイすることを選べます。これは Configuration から
SessionFactory を生成するコードを無くすことができます。コンテナは HibernateService を
起動し、サービスの依存を理想的に管理します(データソースは Hibernate やその他が起動する前に
使用できるようにしなければなりません)。
Depending on your environment, you might have to set the configuration option
hibernate.connection.aggressive_release to true if your application server shows
"connection containment" exceptions.
3.8.1. トランザクション戦略設定
Hibernate Session API は、アーキテクチャ内のシステムの管轄であるあらゆるトランザクションに依存
しません。もしコネクションプールの JDBC を直接使いたい場合、 JDBC API から トランザクションを呼
ぶことができます。もし、 J2EE アプリケーションサーバーで動作させるなら、 Bean 管理トランザク
ションを使い、必要に応じて UserT ransaction を JT A API から呼ぶことになるでしょう。
2つ(それ以上)の環境で互換性のあるコードを維持するために、オプションとして根本的なシステムを
ラッピングする Hibernate T ransaction API を推奨します。 Hibernate 設定プロパティの
hibernate.transaction.factory_class を設定することで、ある特定の T ransaction クラス
のインスタンスを持つことができます。
3つの基本的な(既にある)選択を挙げます:
org.hibernate.transaction.JDBCT ransactionFactory
データベース (JDBC) トランザクションに委譲します(デフォルト)
org.hibernate.transaction.JT AT ransactionFactory
もし、このコンテキスト(例えば、 EJB セッション Bean メソッド)で進行中のトランザクショ
ンが存在する場合、コンテナ管理トランザクションに委譲します。そうでない場合は、新しいト
ランザクションが開始されており、 Bean 管理トランザクションが使われます。
org.hibernate.transaction.CMT T ransactionFactory
コンテナ管理 JT A トランザクションに委譲します
52
第3章 設定
自分自身のトランザクション戦略(例えば、 CORBA トランザクションサービス)を定義することもでき
ます。
Hibernate のいくつかの機能(例えば、二次キャッシュ、 JT A によるコンテキストセッション等)は管理
された環境の中の JT A T ransactionManager へのアクセスを要求します。 J2EE がひとつのメカニズ
ムに規格化されていないので、アプリケーションサーバーにおいて、 Hibernateが
T ransactionManager のリファレンスを取得する方法を明確にする必要があります。
表 3.10 JT A トランザクションマネージャ
T ransaction Factory
Application Server
org.hibernate.transaction.JBossT rans
actionManagerLookup
JBoss
org.hibernate.transaction.WeblogicT r
ansactionManagerLookup
Weblogic
org.hibernate.transaction.WebSphereT
ransactionManagerLookup
WebSphere
org.hibernate.transaction.WebSphereE
xtendedJT AT ransactionLookup
WebSphere 6
org.hibernate.transaction.OrionT rans
actionManagerLookup
Orion
org.hibernate.transaction.ResinT rans
actionManagerLookup
Resin
org.hibernate.transaction.JOT MT ransa
ctionManagerLookup
JOT M
org.hibernate.transaction.JOnAST rans
actionManagerLookup
JOnAS
org.hibernate.transaction.JRun4 T rans
actionManagerLookup
JRun4
org.hibernate.transaction.BEST ransac
tionManagerLookup
Borland ES
3.8.2. SessionFactory の JNDI への登録
JNDI に登録した Hibernate SessionFactory はファクトリのルックアップと新しい Session の作成を
簡易化します。これは JNDI に登録された Datasource には関連せず、両方とも単に同じ登録を使うこ
とに注意してください。
もし SessionFactory を JNDI ネームスペースに登録したい場合、特別な名前(例えば、
java:hibernate/SessionFactory )を hibernate.session_factory_nam e プロパティに
使ってください。もしこのプロパティを省略した場合、 SessionFactory は JNDI に登録されません。
(これは T omcat のようなデフォルト実装で JNDI が読み取り専用の環境の場合は特に便利です。)
SessionFactory を JNDI に登録するとき、 Hibernate は hibernate.jndi.url の値を使用
し、hibernate.jndi.class をイニシャルコンテキストとして具体化します。もし何も設定しない場
合は、デフォルトの InitialContext を使用します。
cfg.buildSessionFactory() をコール後 Hibernate は自動的に SessionFactory を JNDI に配置
します。 HibernateService と一緒に JMX デプロイメントを使わない限り、これはこの呼び出しをア
プリケーション内の何らかのスタートアップコード(もしくはユーティリティクラス) に配置しなければ
53
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
ならないことを意味します(後で議論します)。
もし JNDI SessionFactory を使う場合、 EJB や他のクラスは JNDI ルックアップを使って
SessionFactory を取得します。
管理された環境では SessionFactory を JNDI にバインドし、そうでなければ static シングルトンを
使うことを推奨します。こういった詳細からアプリケーションコードを保護するために、
HibernateUtil.getSessionFactory() のようなヘルパークラスの中に、 SessionFactory を
ルックアップするコードを隠すことを推奨します。このようなヘルパークラスは Hibernate を開始する便
利な手段でもあります。 - 1章を参照してください。
3.8.3. JTA による現在のセッションコンテキストマネージメント
T he easiest way to handle Session s and transactions is Hibernates automatic "current" Session
management. See the discussion of 「コンテキスト上のセッション」 current sessions. Using the
"jta" session context, if there is no Hibernate Session associated with the current JT A transaction,
one will be started and associated with that JT A transaction the first time you call
sessionFactory.getCurrentSession(). T he Session s retrieved via getCurrentSession()
in "jta" context will be set to automatically flush before the transaction completes, close after the
transaction completes, and aggressively release JDBC connections after each statement. T his allows
the Session s to be managed by the lifecycle of the JT A transaction to which it is associated, keeping
user code clean of such management concerns. Your code can either use JT A programmatically through
UserT ransaction, or (recommended for portable code) use the Hibernate T ransaction API to set
transaction boundaries. If you run in an EJB container, declarative transaction demarcation with CMT is
preferred.
3.8.4. JMX デプロイメント
SessionFactory を JNDI から取得するためには cfg.buildSessionFactory() 行をどこかで実行
していなければなりません。あなたはこれを、 static 初期化ブロック内( HibernateUtil のよう
な)か managed service として Hibernate をデプロイするか、どちらかで実行できます。
JBoss AS のような JMX の機能でアプリケーションサーバーにデプロイするために
org.hibernate.jm x.HibernateService を使って、配置します。実際のデプロイメントと設定は
ベンダー特有です。ここで例として JBoss 4.0.x 用の jboss-service.xm l を示します。
54
第3章 設定
<?xml version="1.0"?>
<server>
<mbean code="org.hibernate.jmx.HibernateService"
name="jboss.jca:service=HibernateFactory,name=HibernateFactory">
<!-- Required services -->
<depends>jboss.jca:service=RARDeployer</depends>
<depends>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>
<!-- Bind the Hibernate service to JNDI -->
<attribute name="JndiName">java:/hibernate/SessionFactory</attribute>
<!-- Datasource settings -->
<attribute name="Datasource">java:HsqlDS</attribute>
<attribute name="Dialect">org.hibernate.dialect.HSQLDialect</attribute>
<!-- Transaction integration -->
<attribute name="TransactionStrategy">
org.hibernate.transaction.JTATransactionFactory</attribute>
<attribute name="TransactionManagerLookupStrategy">
org.hibernate.transaction.JBossTransactionManagerLookup</attribute>
<attribute name="FlushBeforeCompletionEnabled">true</attribute>
<attribute name="AutoCloseSessionEnabled">true</attribute>
<!-- Fetching options -->
<attribute name="MaximumFetchDepth">5</attribute>
<!-- Second-level caching -->
<attribute name="SecondLevelCacheEnabled">true</attribute>
<attribute
name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</attribute>
<attribute name="QueryCacheEnabled">true</attribute>
<!-- Logging -->
<attribute name="ShowSqlEnabled">true</attribute>
<!-- Mapping files -->
<attribute
name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>
</mbean>
</server>
このファイルは MET A-INF ディレクトリに配置され、 JAR ファイルを拡張した .sar (service archive)
でパッケージ化されます。同様に Hibernate パッケージも必要です。また、 Hibernate はサードパーティ
のライブラリも要求します。コンパイルした永続化クラスとそのマッピングファイルも同様にアーカイブ
(.sarファイル)に入れます。エンタープライズ Bean (通常はセッション Bean )は自身の JAR ファイ
ルを保持しますが、1回で(ホット)デプロイ可能なユニットのためにメインサービスアーカイブとして
この EJB JAR ファイルを含めることができます。 JBoss AS のドキュメントに JXM サービスと EJB デプ
ロイメントのより多くの情報があります。
55
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 4章 永続クラス
永続クラスはビジネス上の問題のエンティティ(例えば、 E コマースアプリケーションの顧客や注文)
を実装するアプリケーションのクラスです。永続クラスのすべてのインスタンスが永続状態であると見な
されるわけではありません。インスタンスは逆に一時的(transient)であったり、分離状態(detached)
であったりするかもしれません。
Plain Old Java Object (POJO)プログラミングモデルとしても知られるいくつかの単純なルールに従うな
ら、 Hibernate は最もよく働きます。しかしこれらのルールは難しいものではありません。実際
Hibernate3 は永続オブジェクトの性質にほとんど何の前提も置いていません。ドメインモデルは他の方法
で表現することもできます。例えば Map インスタンスのツリーを使う方法があります。
4.1. 単 純 な POJO の 例
以下はネコ科の動物を表現する永続クラスです。
56
第4章 永続クラス
package eg;
import java.util.Set;
import java.util.Date;
public class Cat {
private Long id; // identifier
private
private
private
private
private
Date birthdate;
Color color;
char sex;
float weight;
int litterId;
private Cat mother;
private Set kittens = new HashSet();
private void setId(Long id) {
this.id=id;
}
public Long getId() {
return id;
}
void setBirthdate(Date date) {
birthdate = date;
}
public Date getBirthdate() {
return birthdate;
}
void setWeight(float weight) {
this.weight = weight;
}
public float getWeight() {
return weight;
}
public Color getColor() {
return color;
}
void setColor(Color color) {
this.color = color;
}
void setSex(char sex) {
this.sex=sex;
}
public char getSex() {
return sex;
}
void setLitterId(int id) {
this.litterId = id;
}
public int getLitterId() {
return litterId;
}
void setMother(Cat mother) {
this.mother = mother;
57
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
}
public Cat getMother() {
return mother;
}
void setKittens(Set kittens) {
this.kittens = kittens;
}
public Set getKittens() {
return kittens;
}
// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
kitten.setMother(this);
kitten.setLitterId( kittens.size() );
kittens.add(kitten);
}
}
従うべき4つのルールがあります:
4.1.1. 引数のないコンストラクタを実装する
Cat には引数のないコンストラクタがあります。 Hibernate が Constructor.newInstance() を使っ
て永続クラスのインスタンス化を行えるように、すべての永続クラスにはデフォルトコンストラクタ
(public でなくても構いません) がなければなりません。 Hibernate の実行時プロキシ生成のために、少
なくとも package の可視性を持つデフォルトコンストラクタを強くお勧めします。
4.1.2. 識別子プロパティを用意する(オプション)
Cat has a property called id. T his property maps to the primary key column of a database table. T he
property might have been called anything, and its type might have been any primitive type, any primitive
"wrapper" type, java.lang.String or java.util.Date. (If your legacy database table has
composite keys, you can even use a user-defined class with properties of these types - see the section
on composite identifiers later.)
識別子プロパティは厳密にはオプションです。これを省略して、 Hibernate に内部的にオブジェクトの識
別子を追跡させることは可能です。しかしお勧めはしません。
実際に、識別子プロパティを宣言するクラスだけが利用可能な機能がいくつかあります:
T ransitive reattachment for detached objects (cascade update or cascade merge) - see 「連鎖的な
永続化」
Session.saveOrUpdate()
Session.m erge()
永続クラスには、一貫した名前の識別子プロパティを定義することをお勧めします。さらに null 値を取れ
る(つまりプリミティブではない)型を使った方がよいでしょう。
4.1.3. final クラスにしない(オプション)
Hibernate の中心的な特徴である プロキシ は、永続クラスが final でないこと、またはメソッドを全部
public で宣言しているインターフェースが実装されているかに依存しています。
You can persist final classes that do not implement an interface with Hibernate, but you won't be able
to use proxies for lazy association fetching - which will limit your options for performance tuning.
58
第4章 永続クラス
You should also avoid declaring public final methods on the non-final classes. If you want to use a
class with a public final method, you must explicitly disable proying by setting lazy="false".
4.1.4. 永続フィールドに対するアクセサとミューテータを定義する(オプション)
Cat ではすべての永続フィールドに対してアクセサメソッドを定義しています。他の多くの ORM ツール
は、永続インスタンス変数を直接永続化します。私たちはリレーショナルスキーマとクラスの内部構造を
分離する方が良いと信じています。デフォルトでは、 Hibernate は JavaBean スタイルのプロパティを永
続化し、 getFoo, isFoo, setFoo 形式のメソッド名を認識します。しかし必要なら、特定のプロパティ
に対して、直接のフィールドアクセスに切り替えることは可能です。
プロパティは public で宣言する必要は ありません 。 Hibernate はデフォルトで、 protected もしくは
private の get / set のペアを持つプロパティを永続化することができます。
4.2. 継 承 の 実 装
サブクラスも1番目と2番目のルールを守らなければなりません。サブクラスはスーパークラス Cat から
識別子プロパティを継承します。
package eg;
public class DomesticCat extends Cat {
private String name;
public String getName() {
return name;
}
protected void setName(String name) {
this.name=name;
}
}
4.3. equals() と hashCode() の 実 装
以下の条件の場合、 equals() と hashCode() メソッドをオーバーライドしなければなりません、
永続クラスのインスタンスを Set に置く場合。 (これは多値の関連を表現するおすすめの方法です)
そして同時に
分離インスタンスをセッションへ再追加する場合。
Hibernate は、永続 ID (データベースの行)と、特定のセッションスコープ内に限定ですが Java ID とが
等価であることを保証します。ですから異なるセッションで検索したインスタンスを組み合わせる場合、
Set に意味のあるセマンティクスを持たせようと思っているならすぐに equals() と hashCode() を実
装しなければなりません。
T he most obvious way is to implement equals()/hashCode() by comparing the identifier value of both
objects. If the value is the same, both must be the same database row, they are therefore equal (if both
are added to a Set, we will only have one element in the Set). Unfortunately, we can't use that approach
with generated identifiers! Hibernate will only assign identifier values to objects that are persistent, a
newly created instance will not have any identifier value! Furthermore, if an instance is unsaved and
currently in a Set, saving it will assign an identifier value to the object. If equals() and hashCode()
are based on the identifier value, the hash code would change, breaking the contract of the Set. See the
Hibernate website for a full discussion of this problem. Note that this is not a Hibernate issue, but normal
Java semantics of object identity and equality.
59
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
ビジネスキーの等価性 を使って、 equals() と hashCode() を実装することをお勧めします。ビジネ
スキーの等価性とは、 equals() メソッドが、ビジネスキー、つまり現実の世界においてインスタンスを
特定するキー(自然 候補キー) を形成するプロパティだけを比較することを意味します。
public class Cat {
...
public boolean equals(Object other) {
if (this == other) return true;
if ( !(other instanceof Cat) ) return false;
final Cat cat = (Cat) other;
if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
if ( !cat.getMother().equals( getMother() ) ) return false;
return true;
}
public int hashCode() {
int result;
result = getMother().hashCode();
result = 29 * result + getLitterId();
return result;
}
}
Note that a business key does not have to be as solid as a database primary key candidate (see 「オブ
ジェクト識別子を考える」). Immutable or unique properties are usually good candidates for a business
key.
4.4. 動 的 モ デ ル
Note that the following features are currently considered experimental and may change in the near
future.
Persistent entities don't necessarily have to be represented as POJO classes or as JavaBean objects
at runtime. Hibernate also supports dynamic models (using Map s of Map s at runtime) and the
representation of entities as DOM4J trees. With this approach, you don't write persistent classes, only
mapping files.
By default, Hibernate works in normal POJO mode. You may set a default entity representation mode for
a particular SessionFactory using the default_entity_m ode configuration option (see 表
3.3「Hibernate 設定プロパティ」.
以下の例では Map を使った表現を紹介します。まずマッピングファイルで、クラス名の代わりに(また
はそれに加えて) entity-nam e を定義しなければなりません:
60
第4章 永続クラス
<hibernate-mapping>
<class entity-name="Customer">
<id name="id"
type="long"
column="ID">
<generator class="sequence"/>
</id>
<property name="name"
column="NAME"
type="string"/>
<property name="address"
column="ADDRESS"
type="string"/>
<many-to-one name="organization"
column="ORGANIZATION_ID"
class="Organization"/>
<bag name="orders"
inverse="true"
lazy="false"
cascade="all">
<key column="CUSTOMER_ID"/>
<one-to-many class="Order"/>
</bag>
</class>
</hibernate-mapping>
関連がターゲットのクラス名を使って定義していたとしても、関連のターゲット型も POJO ではなく動的
なエンティティでも構わないことに注意してください。
SessionFactory に対してデフォルトのエンティティモードを dynam ic-m ap に設定した後、実行時
に Map の Map を使うことができます:
61
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
Session s = openSession();
Transaction tx = s.beginTransaction();
Session s = openSession();
// Create a customer
Map david = new HashMap();
david.put("name", "David");
// Create an organization
Map foobar = new HashMap();
foobar.put("name", "Foobar Inc.");
// Link both
david.put("organization", foobar);
// Save both
s.save("Customer", david);
s.save("Organization", foobar);
tx.commit();
s.close();
動的なマッピングの利点は、エンティティクラスの実装を必要としないため、プロトタイピングに要する
ターンアラウンドタイムが早いということです。しかしコンパイル時の型チェックがないので、実行時に
非常に多くの例外処理を扱わなければならないでしょう。 Hibernate マッピングのおかげで、データベー
ススキーマは容易に正規化でき、健全になり、後で適切なドメインモデルの実装を追加することが可能に
なります。
エンティティ表現モードは Session ごとに設定することも可能です。
Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
// Create a customer
Map david = new HashMap();
david.put("name", "David");
dynamicSession.save("Customer", david);
...
dynamicSession.flush();
dynamicSession.close()
...
// Continue on pojoSession
Please note that the call to getSession() using an EntityMode is on the Session API, not the
SessionFactory. T hat way, the new Session shares the underlying JDBC connection, transaction,
and other context information. T his means you don't have tocall flush() and close() on the
secondary Session, and also leave the transaction and connection handling to the primary unit of work.
More information about the XML representation capabilities can be found in 18章XML マッピング.
4.5. Tuplizer
org.hibernate.tuple.T uplizer, and its sub-interfaces, are responsible for managing a particular
representation of a piece of data, given that representation's org.hibernate.EntityMode. If a given
piece of data is thought of as a data structure, then a tuplizer is the thing which knows how to create
such a data structure and how to extract values from and inject values into such a data structure. For
example, for the POJO entity mode, the correpsonding tuplizer knows how create the POJO through its
62
第4章 永続クラス
constructor and how to access the POJO properties using the defined property accessors. T here are
two high-level types of T uplizers, represented by the
org.hibernate.tuple.entity.EntityT uplizer and
org.hibernate.tuple.com ponent.Com ponentT uplizer interfaces. EntityT uplizer s are
responsible for managing the above mentioned contracts in regards to entities, while
Com ponentT uplizer s do the same for components.
ユーザーは独自の T uplizer に差し替えることも可能です。おそらく dynamic-map entity-mode の際に
java.util.HashMap を使うのではなく、 java.util.Map の実装が必要でしょう。もしくは、おそら
くデフォルトのものではなく、別のプロキシ生成戦略の定義が必要でしょう。両者とも、カスタムの
T uplizer 実装を定義することで達成されます。 T uplizer の定義は、管理しようとするエンティティやコン
ポーネントのマッピングに結び付けられます。顧客エンティティの例は以下になります:
<hibernate-mapping>
<class entity-name="Customer">
<!-Override the dynamic-map entity-mode
tuplizer for the customer entity
-->
<tuplizer entity-mode="dynamic-map"
class="CustomMapTuplizerImpl"/>
<id name="id" type="long" column="ID">
<generator class="sequence"/>
</id>
<!-- other properties -->
...
</class>
</hibernate-mapping>
public class CustomMapTuplizerImpl
extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer {
// override the buildInstantiator() method to plug in our custom map...
protected final Instantiator buildInstantiator(
org.hibernate.mapping.PersistentClass mappingInfo) {
return new CustomMapInstantiator( mappingInfo );
}
private static final class CustomMapInstantiator
extends org.hibernate.tuple.DynamicMapInstantitor {
// override the generateMap() method to return our custom map...
protected final Map generateMap() {
return new CustomMap();
}
}
}
63
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 5章 基本的な O/R マッピング
5.1. マ ッ ピ ン グ 定 義
オブジェクト/リレーショナルマッピングは通常 XML ドキュメントで定義します。マッピングドキュメン
トは、読みやすく手作業で編集しやすいようにデザインされています。マッピング言語は Java 中心、つ
まりテーブル定義ではなく永続クラスの定義に基づいて構築されています。
多くの Hibernate ユーザーは XML マッピングの記述を手作業で行いますが、 XDoclet, Middlegen,
AndroMDA というようなマッピングドキュメントを生成するツールがいくつか存在することを覚えておい
てください。
サンプルのマッピングから始めましょう:
64
第5章 基本的な O/R マッピング
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Cat"
table="cats"
discriminator-value="C">
<id name="id">
<generator class="native"/>
</id>
<discriminator column="subclass"
type="character"/>
<property name="weight"/>
<property name="birthdate"
type="date"
not-null="true"
update="false"/>
<property name="color"
type="eg.types.ColorUserType"
not-null="true"
update="false"/>
<property name="sex"
not-null="true"
update="false"/>
<property name="litterId"
column="litterId"
update="false"/>
<many-to-one name="mother"
column="mother_id"
update="false"/>
<set name="kittens"
inverse="true"
order-by="litter_id">
<key column="mother_id"/>
<one-to-many class="Cat"/>
</set>
<subclass name="DomesticCat"
discriminator-value="D">
<property name="name"
type="string"/>
</subclass>
</class>
<class name="Dog">
65
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping>
マッピングドキュメントの内容を説明します。ただし、ここでは Hibernate が実行時に使うドキュメント
要素と属性についてのみ説明します。マッピングドキュメントは、いくつかのオプション属性と要素を含
んでいます(例えば not-null 属性)。それらはスキーマエクスポートツールが出力するデータベース
スキーマに影響を与えるものです。
5.1.1. Doctype
XML マッピングでは、お見せしたようなドキュメント型を必ず定義すべきです。実際の DT D は、上記の
URL の hibernate-x.x.x/src/org/hibernate ディレクトリ、または hibernate.jar 内にあり
ます。 Hibernate は常に、そのクラスパス内で DT D を探し始めます。インターネットにある DT D ファイ
ルを探そうとしたなら、クラスパスの内容を見て、 DT D 宣言を確認してください。
5.1.1.1. エンティティリゾルバ
前述したように、 Hibernate はまずクラスパス内で DT D を解決しようとします。
org.xm l.sax.EntityResolver のカスタム実装を XML ファイルを読み込むための SAXReader に登
録することによって、 DT D を解決します。このカスタムの EntityResolver は2つの異なるシステム
ID 名前空間を認識します。
hibernate nam espace は、リゾルバが http://hibernate.sourceforge.net/ で始まるシ
ステム ID に到達したときに認識されます。そしてリゾルバは、 Hibernate のクラスをロードしたクラ
スローダを用いて、これらのエンティティを解決しようとします。
user nam espace は、リゾルバが URL プロトコルの classpath:// を使ったシステム ID に到達
したときに、認識されます。そしてリゾルバは、 (1) カレントスレッドのコンテキストクラスロー
ダー、または (2) Hibernate のクラスをロードしたクラスローダを使って、これらのエンティティを解
決しようとします。
下記は、ユーザー名前空間を使った例です:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
<!ENTITY types SYSTEM "classpath://your/domain/types.xml">
]>
<hibernate-mapping package="your.domain">
<class name="MyEntity">
<id name="id" type="my-custom-id-type">
...
</id>
</class>
& types;
</hibernate-mapping>
Where types.xm l is a resource in the your.dom ain package and contains a custom 「カスタム型」
typedef.
5.1.2. hibernate-mapping
この要素にはいくつかオプション属性があります。 schem a 属性と catalog 属性は、このマッピングが
参照するテーブルが、この属性によって指定されたスキーマと(または)カタログに属することを指定しま
66
第5章 基本的な O/R マッピング
す。この属性が指定されると、テーブル名は与えられたスキーマ名とカタログ名で修飾されます。これら
の属性が指定されていなければ、テーブル名は修飾されません。 default-cascade 属性は、
cascade 属性を指定していないプロパティやコレクションに、どのカスケードスタイルを割り当てるか
を指定します。 auto-im port 属性は、クエリ言語内で修飾されていないクラス名を、デフォルトで使え
るようにします。
<hibernate-mapping
schema="schemaName"
catalog="catalogName"
default-cascade="cascade_style"
default-access="field|property|ClassName"
default-lazy="true|false"
auto-import="true|false"
package="package.name"
/>
schem a(オプション):データベーススキーマの名前。
catalog (オプション):データベースカタログの名前。
default-cascade (オプション - デフォルトは none): デフォルトのカスケードスタイル。
default-access (オプション - デフォルトは property ): Hibernate がプロパティにアクセスする
際に取るべき戦略。 PropertyAccessor を実装することでカスタマイズ可能。
default-lazy (オプション - デフォルトは true ): lazy 属性が指定されていないクラスやコレクショ
ンマッピングに対するデフォルト値。
auto-im port (オプション - デフォルトは true):クエリ言語内で、(このマッピング内のクラス
の)修飾されていないクラス名を使えるかどうかを指定します。
package (オプション): マッピングドキュメント内で修飾されていないクラス名に対して割り当てる、
パッケージの接頭辞 (prefix) を指定します。
If you have two persistent classes with the same (unqualified) name, you should set autoim port="false". Hibernate will throw an exception if you attempt to assign two classes to the same
"imported" name.
Note that the hibernate-m apping element allows you to nest several persistent <class> mappings,
as shown above. It is however good practice (and expected by some tools) to map only a single
persistent class (or a single class hierarchy) in one mapping file and name it after the persistent
superclass, e.g. Cat.hbm .xm l, Dog.hbm .xm l, or if using inheritance, Anim al.hbm .xm l.
5.1.3. class
class 要素を使って、永続クラスを宣言できます:
67
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class
name="ClassName"
table="tableName"
discriminator-value="discriminator_value"
mutable="true|false"
schema="owner"
catalog="catalog"
proxy="ProxyInterface"
dynamic-update="true|false"
dynamic-insert="true|false"
select-before-update="true|false"
polymorphism="implicit|explicit"
where="arbitrary sql where condition"
persister="PersisterClass"
batch-size="N"
optimistic-lock="none|version|dirty|all"
lazy="true|false"
entity-name="EntityName"
check="arbitrary sql check condition"
rowid="rowid"
subselect="SQL expression"
abstract="true|false"
node="element-name"
/>
nam e (オプション):永続クラス(またはインターフェース)の完全修飾 Java クラス名。もしこの属性が
欠落している場合、 POJO ではないエンティティに対するマッピングとして扱われます。
table (オプション - デフォルトは修飾されていないクラス名):データベーステーブルの名前
discrim inator-value (オプション - デフォルトはクラス名): ポリモーフィックな振る舞いに使われ
る個々のサブクラスを識別するための値。値は null か not null のいずれかを取ります。
m utable (オプション、デフォルトは true ): そのクラスのインスタンスが更新可能(または不可能)
であることを指定します。
schem a (optional): Override the schema name specified by the root <hibernate-m apping> element.
catalog (optional): Override the catalog name specified by the root <hibernate-m apping>
element.
proxy (オプション):遅延初期化プロキシに使うインターフェースを指定します。永続化するクラス名
そのものを指定することも可能です。
dynam ic-update (オプション、 デフォルトは false ):値が変更されたカラムだけを含む SQL の
UPDAT E 文を、実行時に生成することを指定します。
dynam ic-insert (オプション, デフォルトは false ):値が null ではないカラムだけを含む SQL の
INSERT 文を、実行時に生成することを指定します。
select-before-update (オプション、デフォルトは false): オブジェクトが変更されたのが確実でな
いならば、 Hibernate が SQL の UPDAT E を 決して実行しない ことを指定します。ある特定の場合(実際
的には、一時オブジェクトが update() を使い、新しいセッションと関連付けられた時だけ)、 UPDAT E
が実際に必要かどうかを決定するために、 Hibernate が余分な SQL の SELECT 文を実行することを意味
します。
polym orphism (オプション、デフォルトでは im plicit ): implicit(暗黙)かexplicit(明示)の、どち
68
第5章 基本的な O/R マッピング
らのクエリポリモーフィズムを使うか決定します。
where (オプション): このクラスのオブジェクトを検索するときに使用する、任意の SQL の WHERE
条件を指定します。
persister (オプション):カスタム ClassPersister を指定します。
batch-size (optional, defaults to 1) specify a "batch size" for fetching instances of this class by
identifier.
optim istic-lock (オプション、デフォルトは version ): 楽観ロック戦略を決定します。
lazy (optional): Lazy fetching may be completely disabled by setting lazy="false".
entity-nam e (optional, defaults to the class name): Hibernate3 allows a class to be mapped multiple
times (to different tables, potentially), and allows entity mappings that are represented by Maps or XML
at the Java level. In these cases, you should provide an explicit arbitrary name for the entity. See 「動的
モデル」 and 18章XML マッピング for more information.
check (オプション):自動的にスキーマを生成するために、複数行の check 制約を生成する SQL 式。
rowid (オプション): Hibernate は、それをサポートしているデータベースで ROWID と 呼ばれるも
のを使うことができます。例えば Oracle を使っているとき、このオプションに rowid を設定すれば、
Hiberante は update を高速化するために rowid という特別なカラムを使うことができます。 ROWID は
詳細な実装であり、保存されたタプルの物理的な位置を表しています。
subselect (optional): Maps an immutable and read-only entity to a database subselect. Useful if you
want to have a view instead of a base table, but don't. See below for more information.
abstract (optional): Used to mark abstract superclasses in <union-subclass> hierarchies.
It is perfectly acceptable for the named persistent class to be an interface. You would then declare
implementing classes of that interface using the <subclass> element. You may persist any static inner
class. You should specify the class name using the standard form ie. eg.Foo$Bar.
Immutable classes, m utable="false", may not be updated or deleted by the application. T his allows
Hibernate to make some minor performance optimizations.
T he optional proxy attribute enables lazy initialization of persistent instances of the class. Hibernate
will initially return CGLIB proxies which implement the named interface. T he actual persistent object will
be loaded when a method of the proxy is invoked. See "Proxies for Lazy Initialization" below.
Implicit polymorphism means that instances of the class will be returned by a query that names any
superclass or implemented interface or the class and that instances of any subclass of the class will be
returned by a query that names the class itself. Explicit polymorphism means that class instances will be
returned only by queries that explicitly name that class and that queries that name the class will return
only instances of subclasses mapped inside this <class> declaration as a <subclass> or <joinedsubclass>. For most purposes the default, polym orphism ="im plicit", is appropriate. Explicit
polymorphism is useful when two different classes are mapped to the same table (this allows a
"lightweight" class that contains a subset of the table columns).
T he persister attribute lets you customize the persistence strategy used for the class. You may, for
example, specify your own subclass of org.hibernate.persister.EntityPersister or you
might even provide a completely new implementation of the interface
org.hibernate.persister.ClassPersister that implements persistence via, for example, stored
procedure calls, serialization to flat files or LDAP. See org.hibernate.test.Custom Persister for
69
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
procedure calls, serialization to flat files or LDAP. See org.hibernate.test.Custom Persister for
a simple example (of "persistence" to a Hashtable).
Note that the dynam ic-update and dynam ic-insert settings are not inherited by subclasses and
so may also be specified on the <subclass> or <joined-subclass> elements. T hese settings may
increase performance in some cases, but might actually decrease performance in others. Use
judiciously.
select-before-update の使用は通常パフォーマンスを落とします。もし Session へ分離インスタ
ンスのグラフを再追加するなら、データベース更新のトリガを不必要に呼び出すのを避けるという点で、
非常に有用です。
dynam ic-update を有効にすれば、楽観ロック戦略を選ぶことになります:
version バージョン/タイムスタンプカラムをチェックします。
all すべてのカラムをチェックします。
dirty 変更したカラムをチェックし、同時更新できるようにします。
none 楽観ロックを使用しません。
Hibernate で楽観的ロック戦略を使うなら、バージョン/タイムスタンプカラムを使うことを 非常に 強く
お勧めします。楽観的ロックはパフォーマンスの観点からも最適であり、さらに分離インスタンスへの修
正 (つまり Session.m arge() が使われるとき) を正確に扱うことのできる唯一の戦略でもありま
す。
T here is no difference between a view and a base table for a Hibernate mapping, as expected this is
transparent at the database level (note that some DBMS don't support views properly, especially with
updates). Sometimes you want to use a view, but can't create one in the database (ie. with a legacy
schema). In this case, you can map an immutable and read-only entity to a given SQL subselect
expression:
<class name="Summary">
<subselect>
select item.name, max(bid.amount), count(*)
from item
join bid on bid.item_id = item.id
group by item.name
</subselect>
<synchronize table="item"/>
<synchronize table="bid"/>
<id name="name"/>
...
</class>
Declare the tables to synchronize this entity with, ensuring that auto-flush happens correctly, and that
queries against the derived entity do not return stale data. T he <subselect> is available as both as an
attribute and a nested mapping element.
5.1.4. id
Mapped classes must declare the primary key column of the database table. Most classes will also have
a JavaBeans-style property holding the unique identifier of an instance. T he <id> element defines the
mapping from that property to the primary key column.
70
第5章 基本的な O/R マッピング
<id
name="propertyName"
type="typename"
column="column_name"
unsaved-value="null|any|none|undefined|id_value"
access="field|property|ClassName">
node="element-name|@attribute-name|element/@attribute|."
<generator class="generatorClass"/>
</id>
nam e(オプション):識別子プロパティの名前。
type(オプション): Hibernate の型を示す名前。
colum n(オプション - デフォルトはプロパティ名): 主キーカラムの名前。
unsaved-value (optional - defaults to a "sensible" value): An identifier property value that indicates
that an instance is newly instantiated (unsaved), distinguishing it from detached instances that were
saved or loaded in a previous session.
access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用
すべき戦略。
nam e 属性がなければ、クラスには識別子プロパティがないものとみなされます。
unsaved-value 属性は Hibernate3 ではほとんどの場合、必要ではありません。
T here is an alternative <com posite-id> declaration to allow access to legacy data with composite
keys. We strongly discourage its use for anything else.
5.1.4 .1. ジェネレータ
T he optional <generator> child element names a Java class used to generate unique identifiers for
instances of the persistent class. If any parameters are required to configure or initialize the generator
instance, they are passed using the <param > element.
<id name="id" type="long" column="cat_id">
<generator class="org.hibernate.id.TableHiLoGenerator">
<param name="table">uid_table</param>
<param name="column">next_hi_value_column</param>
</generator>
</id>
すべてのジェネレータは、 org.hibernate.id.IdentifierGenerator インターフェースを実装し
ます。これはとても単純なインターフェースなので、特別な実装を独自に用意するアプリケーションもあ
るかもしれません。しかし Hibernate は組み込みの実装をいくつも用意しています。組み込みのジェネ
レータには以下のショートカット名があります:
increm ent
long , short , int 型の識別子を生成します。これらは他のプロセスが同じテーブルにデータを
挿入しないときだけユニークです。 クラスタ内では使わないでください 。
identity
DB2, MySQL, MS SQL Server, Sybase, HypersonicSQL の識別子カラムをサポートします。返さ
れる識別子の型は long , short , int のいずれかです。
71
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
sequence
DB2, PostgreSQL, Oracle, SAP DB, McKoi のシーケンスや、 Interbase のジェネレータを使用し
ます。返される識別子の型は long , short , int のいずれかです。
hilo
uses a hi/lo algorithm to efficiently generate identifiers of type long, short or int, given a
table and column (by default hibernate_unique_key and next_hi respectively) as a
source of hi values. T he hi/lo algorithm generates identifiers that are unique only for a particular
database.
seqhilo
long , short , int 型の識別子を効率的に生成する hi/lo アルゴリズムを使います。指定された
データベースシーケンスを与えます。
uuid
( IP アドレスが使用される)ネットワーク内でユニークな文字列型の識別子を生成するために、
128 ビットの UUID アルゴリズムを使用します。 UUID は長さ 32 の 16 進数字の文字列としてエ
ンコードされます。
guid
MS SQL サーバーと MySQL でデータベースが生成する GUID 文字列を使用します。
native
使用するデータベースの性能により identity 、 sequence 、 hilo のいずれかが選ばれま
す。
assigned
lets the application to assign an identifier to the object before save() is called. T his is the
default strategy if no <generator> element is specified.
select
あるユニークキーによる行の選択と主キーの値の復元により、データベーストリガが割り当てた
主キーを取得します。
foreign
uses the identifier of another associated object. Usually used in conjunction with a <one-toone> primary key association.
sequence-identity
実際の値の生成のためにデータベースシーケンスを使用する特別なシーケンス生成戦略ですが、
JDBC3 getGeneratedKeys と結びついて、 INSERT 文の実行の一部として生成された識別子の
値を実際に返します。この戦略は JDK 1.4 を対象とする Oracle 10g のドライバでサポートされ
ていることが知られています。これらの INSERT 文でのコメントは Oracle のドライバのバグに
より無効にされていることに注意してください。
72
第5章 基本的な O/R マッピング
5.1.4 .2. Hi/lo アルゴリズム
T he hilo and seqhilo generators provide two alternate implementations of the hi/lo algorithm, a
favorite approach to identifier generation. T he first implementation requires a "special" database table to
hold the next available "hi" value. T he second uses an Oracle-style sequence (where supported).
<id name="id" type="long" column="cat_id">
<generator class="hilo">
<param name="table">hi_value</param>
<param name="column">next_value</param>
<param name="max_lo">100</param>
</generator>
</id>
<id name="id" type="long" column="cat_id">
<generator class="seqhilo">
<param name="sequence">hi_value</param>
<param name="max_lo">100</param>
</generator>
</id>
Unfortunately, you can't use hilo when supplying your own Connection to Hibernate. When
Hibernate is using an application server datasource to obtain connections enlisted with JT A, you must
properly configure the hibernate.transaction.m anager_lookup_class.
5.1.4 .3. UUID アルゴリズム
T he UUID contains: IP address, startup time of the JVM (accurate to a quarter second), system time and
a counter value (unique within the JVM). It's not possible to obtain a MAC address or memory address
from Java code, so this is the best we can do without using JNI.
5.1.4 .4 . 識別子カラムとシーケンス
識別子カラムをサポートしているデータベース(DB2, MySQL, Sybase, MS SQL)では、 identity キー
生成が使えます。シーケンスをサポートするデータベース(DB2, Oracle, PostgreSQL, Interbase, McKoi,
SAP DB)では、 sequence スタイルのキー生成が使えます。どちらの戦略も、新しいオブジェクトを挿
入するために、 SQL クエリを2つ必要とします。
<id name="id" type="long" column="person_id">
<generator class="sequence">
<param name="sequence">person_id_sequence</param>
</generator>
</id>
<id name="id" type="long" column="person_id" unsaved-value="0">
<generator class="identity"/>
</id>
クロスプラットフォームの開発では、native 戦略は identity 、 sequence 、 hilo 戦略の中から1
つを選択しますが、これは使用しているデータベースの能力に依存します。
5.1.4 .5. 識別子の割り当て
If you want the application to assign identifiers (as opposed to having Hibernate generate them), you
73
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
may use the assigned generator. T his special generator will use the identifier value already assigned
to the object's identifier property. T his generator is used when the primary key is a natural key instead of
a surrogate key. T his is the default behavior if you do no specify a <generator> element.
Choosing the assigned generator makes Hibernate use unsaved-value="undefined", forcing
Hibernate to go to the database to determine if an instance is transient or detached, unless there is a
version or timestamp property, or you define Interceptor.isUnsaved().
5.1.4 .6. トリガにより割り当てられた主キー
レガシースキーマのためにのみ指定します( Hibernate はトリガを使って DDL を生成しません)。
<id name="id" type="long" column="person_id">
<generator class="select">
<param name="key">socialSecurityNumber</param>
</generator>
</id>
上記の例の中で、クラスで自然キーとして定義された socialSecurityNum ber という名前のユニーク
な値のプロパティと、値がトリガにより生成される person_id という名前の代理キーがあります。
5.1.5. composite-id
<composite-id
name="propertyName"
class="ClassName"
mapped="true|false"
access="field|property|ClassName">
node="element-name|."
<key-property name="propertyName" type="typename" column="column_name"/>
<key-many-to-one name="propertyName class="ClassName"
column="column_name"/>
......
</composite-id>
For a table with a composite key, you may map multiple properties of the class as identifier properties.
T he <com posite-id> element accepts <key-property> property mappings and <key-m any-toone> mappings as child elements.
<composite-id>
<key-property name="medicareNumber"/>
<key-property name="dependent"/>
</composite-id>
複合識別子の等価性を実装するためには、永続クラスが equals() と hashCode() をオーバーライド
しなければなりません 。 また Serializable も実装しなければいけません。
Unfortunately, this approach to composite identifiers means that a persistent object is its own identifier.
T here is no convenient "handle" other than the object itself. You must instantiate an instance of the
persistent class itself and populate its identifier properties before you can load() the persistent state
associated with a composite key. We call this approach an embedded composite identifier, and
discourage it for serious applications.
A second approach is what we call a mapped composite identifier, where the identifier properties named
inside the <com posite-id> element are duplicated on both the persistent class and a separate
74
第5章 基本的な O/R マッピング
identifier class.
<composite-id class="MedicareId" mapped="true">
<key-property name="medicareNumber"/>
<key-property name="dependent"/>
</composite-id>
この例では、複合識別子クラス( MedicareId )とエンティティクラス自身の両方が、
m edicareNum ber と dependent という名前のプロパティを持ちます。識別子クラスは、 equals()
と hashCode() をオーバライドし、 Serializable を実装しなくてはなりません。この方法には、明
らかにコードが重複するという不都合があります。
次の属性はマッピングした複合識別子を指定するために使用します:
m apped (オプション、デフォルトは false ): マッピングした複合識別子が使用されることと、包含
されたプロパティのマッピングが、エンティティクラスと複合識別子クラスの両方を参照することを
示します。
class (オプション、ただしマッピングした複合識別子には必須): 複合識別子として使用するクラス。
We will describe a third, even more convenient approach where the composite identifier is implemented
as a component class in 「複合識別子としてのコンポーネント」. T he attributes described below apply
only to this alternative approach:
nam e (オプション、このアプローチでは必須): 複合識別子を保持するコンポーネントタイプのプロパ
ティ(9章を参照してください)。
access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために
使用すべき戦略。
class (オプション - デフォルトはリフレクションにより決定されるプロパティの型): 複合識別子
として使われるコンポーネントのクラス(次の節を見てください)。
この3つ目の方法は 識別子コンポーネント と呼び、ほとんどすべてのアプリケーションに対して推奨する
方法です。
5.1.6. discriminator
T he <discrim inator> element is required for polymorphic persistence using the table-per-classhierarchy mapping strategy and declares a discriminator column of the table. T he discriminator column
contains marker values that tell the persistence layer what subclass to instantiate for a particular row. A
restricted set of types may be used: string, character, integer, byte, short, boolean, yes_no,
true_false.
<discriminator
column="discriminator_column"
type="discriminator_type"
force="true|false"
insert="true|false"
formula="arbitrary sql expression"
/>
colum n(オプション - デフォルトは class ): 識別カラムの名前。
type (オプション - デフォルトは string ): Hibernate の型を示す名前。
force (optional - defaults to false) "force" Hibernate to specify allowed discriminator values even
when retrieving all instances of the root class.
75
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
insert (オプション - デフォルトは true ): もし識別カラムがマッピングする複合識別子の一部なら
ば、 false と設定してください。 (Hibernate に SQL の INSERT 内のカラムを含ませないよう伝えま
す。)
form ula (オプション) 型が評価されるときに実行される任意の SQL 式。コンテンツベースの識別を可能
にします。
Actual values of the discriminator column are specified by the discrim inator-value attribute of the
<class> and <subclass> elements.
T he force attribute is (only) useful if the table contains rows with "extra" discriminator values that are
not mapped to a persistent class. T his will not usually be the case.
form ula 属性を使うと、行の型を評価するために任意の SQL 式を宣言できます:
<discriminator
formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
type="integer"/>
5.1.7. version(オプション)
T he <version> element is optional and indicates that the table contains versioned data. T his is
particularly useful if you plan to use long transactions (see below).
<version
column="version_column"
name="propertyName"
type="typename"
access="field|property|ClassName"
unsaved-value="null|negative|undefined"
generated="never|always"
insert="true|false"
node="element-name|@attribute-name|element/@attribute|."
/>
colum n (オプション - デフォルトはプロパティ名): バージョン番号を保持するカラムの名前。
nam e :永続クラスのプロパティの名前。
type (オプション - デフォルトは integer ):バージョン番号の型。
access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用
すべき戦略。
unsaved-value (オプション - デフォルトは undefined ): インスタンスが新しくインスタンス化
されたことを示す (セーブされていないことを示す) バージョンプロパティの値。以前の Session で
セーブまたはロードされた一時的なインスタンスと区別するために使います。 ( undefined は識別子
プロパティの値が使われることを指定します。)
generated (optional - defaults to never): Specifies that this version property value is actually
generated by the database. See the discussion of 「生成プロパティ」 generated properties.
insert (オプション - デフォルトは true ): SQLの insert 文にバージョンカラムを含めるべきかどうかを
指定します。もしデータベースカラムのデフォルト値が 0 と定義されるときには、 false に設定すると
良いでしょう。
76
第5章 基本的な O/R マッピング
バージョン番号は Hibernate の long 、 integer 、 short 、 tim estam p 、 calendar 型のいずれ
かです。
バージョンやタイムスタンプのプロパティは、分離されたインスタンスに対して null であってはなりませ
ん。そのためどのような unsaved-value 戦略が指定されても、 Hibernate は null のバージョンやタイ
ムスタンプを持ったすべてのインスタンスを、一時的なものであると判断します。 null を許容するバー
ジョンやタイムスタンプのプロパティを定義することは、 Hibernate において過渡的に一時オブジェクト
とすることを防ぐ簡単な方法です。特に識別子の割り当てや複合キーを使用しているときには特に有用で
す。
5.1.8. timestamp(オプション)
T he optional <tim estam p> element indicates that the table contains timestamped data. T his is
intended as an alternative to versioning. T imestamps are by nature a less safe implementation of
optimistic locking. However, sometimes the application might use the timestamps in other ways.
<timestamp
column="timestamp_column"
name="propertyName"
access="field|property|ClassName"
unsaved-value="null|undefined"
source="vm|db"
generated="never|always"
node="element-name|@attribute-name|element/@attribute|."
/>
colum n(オプション - デフォルトはプロパティ名): タイムスタンプを保持するカラムの名前。
nam e : 永続クラスである Java の Date型または T im estam p 型 の、 JavaBeans スタイルプロパティ
の名前。
access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用
すべき戦略。
unsaved-value (オプション - デフォルトは null ): インスタンスが新しくインスタンス化された
(セーブされていない)ことを示すバージョンプロパティの値。以前の Session でセーブまたはロードさ
れた一時的なインスタンスと区別するために使われます。 ( undefined と指定すると、識別子プロパ
ティの値が使われます。)
source (optional - defaults to vm ): From where should Hibernate retrieve the timestamp value? From the
database, or from the current JVM? Database-based timestamps incur an overhead because Hibernate
must hit the database in order to determine the "next value", but will be safer for use in clustered
environments. Note also, that not all Dialect s are known to support retrieving of the database's
current timestamp, while others might be unsafe for usage in locking due to lack of precision (Oracle 8
for example).
generated (optional - defaults to never): Specifies that this timestamp property value is actually
generated by the database. See the discussion of 「生成プロパティ」 generated properties.
Note that <tim estam p> is equivalent to <version type="tim estam p">. And <tim estam p
source="db"> is equivalent to <version type="dbtim estam p">
5.1.9. property
T he <property> element declares a persistent, JavaBean style property of the class.
77
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<property
name="propertyName"
column="column_name"
type="typename"
update="true|false"
insert="true|false"
formula="arbitrary SQL expression"
access="field|property|ClassName"
lazy="true|false"
unique="true|false"
not-null="true|false"
optimistic-lock="true|false"
generated="never|insert|always"
node="element-name|@attribute-name|element/@attribute|."
index="index_name"
unique_key="unique_key_id"
length="L"
precision="P"
scale="S"
/>
nam e: 小文字で始まるプロパティ名。
colum n (optional - defaults to the property name): the name of the mapped database table column. T his
may also be specified by nested <colum n> element(s).
type(オプション): Hibernate の型を示す名前。
update, insert (optional - defaults to true) : specifies that the mapped columns should be included
in SQL UPDAT E and/or INSERT statements. Setting both to false allows a pure "derived" property
whose value is initialized from some other property that maps to the same colum(s) or by a trigger or
other application.
form ula(オプション): 計算 プロパティのための値を定義する SQL 式。計算されたプロパティは自
身のカラムへのマッピングがありません。
access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用
すべき戦略。
lazy (オプション - デフォルトは false ): インスタンス変数に最初にアクセスしたときに、プロパティ
を遅延して取得するよう指定します。 (バイトコード実装を作成する時間が必要になります)。
unique (オプション):カラムにユニーク制約をつける DDL の生成を可能にします。また、 propertyref のターゲットとすることもできます。
not-null (オプション):カラムに null 値を許可する DDL の生成を可能にします。
optim istic-lock (オプション - デフォルトは true ): このプロパティの更新に楽観ロックの取得を要
求するかどうかを指定します。言い換えれば、このプロパティがダーティであるときにバージョンを増や
すべきかを決定します。
generated (optional - defaults to never): Specifies that this property value is actually generated by
the database. See the discussion of 「生成プロパティ」 generated properties.
typename には以下の値が可能です:
1. Hibernate の基本型の名前(例 integer, string, character, date, tim estam p,
78
第5章 基本的な O/R マッピング
float, binary, serializable, object, blob )。
2. デフォルトの基本型の Java クラス名 (例 int, float, char, java.lang.String,
java.util.Date, java.lang.Integer, java.sql.Clob )。
3. シリアライズ可能な Java クラスの名前。
4. カスタム型のクラス名(例 com .illflow.type.MyCustom T ype )。
型を指定しなければ、 Hibernate は正しい Hibernate の型を推測するために、指定されたプロパティに対
してリフレクションを使います。 Hibernate はルール2, 3, 4をその順序に使い、 getter プロパティの返り
値のクラスの名前を解釈しようとします。しかしこれで常に十分であるとは限りません。場合によって
は、 type 属性が必要な場合があります。 (例えば Hibernate.DAT E と Hibernate.T IMEST AMP を
区別するため、またはカスタム型を指定するためなどです。)
T he access attribute lets you control how Hibernate will access the property at runtime. By default,
Hibernate will call the property get/set pair. If you specify access="field", Hibernate will bypass the
get/set pair and access the field directly, using reflection. You may specify your own strategy for property
access by naming a class that implements the interface
org.hibernate.property.PropertyAccessor.
特に強力な特徴は生成プロパティです。これらのプロパティは当然読み取り専用であり、プロパティの値
はロード時に計算されます。計算を SQL 式として宣言すると、このプロパティはインスタンスをロード
する SQL クエリの SELECT 句のサブクエリに変換されます:
<property name="totalPrice"
formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
WHERE li.productId = p.productId
AND li.customerId = customerId
AND li.orderNumber = orderNumber )"/>
Note that you can reference the entities own table by not declaring an alias on a particular column
(custom erId in the given example). Also note that you can use the nested <form ula> mapping
element if you don't like to use the attribute.
5.1.10. many-to-one
他の永続クラスへの通常の関連は m any-to-one 要素を使って定義します。リレーショナルモデルは多対
一関連です。つまりあるテーブルの外部キーは、ターゲットとなるテーブルの主キーカラムを参照してい
ます。
79
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<many-to-one
name="propertyName"
column="column_name"
class="ClassName"
cascade="cascade_style"
fetch="join|select"
update="true|false"
insert="true|false"
property-ref="propertyNameFromAssociatedClass"
access="field|property|ClassName"
unique="true|false"
not-null="true|false"
optimistic-lock="true|false"
lazy="proxy|no-proxy|false"
not-found="ignore|exception"
entity-name="EntityName"
formula="arbitrary SQL expression"
node="element-name|@attribute-name|element/@attribute|."
embed-xml="true|false"
index="index_name"
unique_key="unique_key_id"
foreign-key="foreign_key_name"
/>
nam e:プロパティ名。
colum n (optional): T he name of the foreign key column. T his may also be specified by nested
<colum n> element(s).
class(オプション - デフォルトはリフレクションにより決定されるプロパティの型): 関連クラスの名
前。
cascade(オプション): どの操作を、親オブジェクトから関連オブジェクトへとカスケードさせるか
を指定します。
fetch(オプション - デフォルトは select ): 外部結合フェッチと順次選択フェッチ(sequential
select fetch)のどちらかを選択します。
update, insert (optional - defaults to true) specifies that the mapped columns should be included
in SQL UPDAT E and/or INSERT statements. Setting both to false allows a pure "derived" association
whose value is initialized from some other property that maps to the same colum(s) or by a trigger or
other application.
property-ref(オプション): この外部キーに結合された関連クラスのプロパティ名。何も指定しな
ければ、関連クラスの主キーが使われます。
access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用
すべき戦略。
unique(オプション): 外部キーカラムに対してユニーク制約をつけた DDL の生成を可能にします。
また、 property-ref のターゲットにすることもできます。これにより関連の多重度を効果的に一対一
にします。
not-null (オプション): 外部キーカラムに対して、 null 値を許可する DDL の生成を可能にします。
optim istic-lock (オプション - デフォルトは true ): このプロパティの更新に楽観的ロックの取得を
要求するかどうかを指定します。言い換えれば、このプロパティがダーティであるときにバージョンを増
80
第5章 基本的な O/R マッピング
やすべきかを決定します。
lazy (optional - defaults to proxy): By default, single point associations are proxied. lazy="noproxy" specifies that the property should be fetched lazily when the instance variable is first accessed
(requires build-time bytecode instrumentation). lazy="false" specifies that the association will
always be eagerly fetched.
not-found (オプション - デフォルトは exception ): 欠落した行を参照する外部キーをどのように扱う
かを指定します。 ignore は欠落した行を null 関連として扱います。
entity-nam e (オプション):関連したクラスのエンティティ名。
form ula (オプション): 計算された 外部キーに対して値を定義する SQL 式
Setting a value of the cascade attribute to any meaningful value other than none will propagate certain
operations to the associated object. T he meaningful values are the names of Hibernate's basic
operations, persist, m erge, delete, save-update, evict, replicate, lock, refresh,
as well as the special values delete-orphan and all and comma-separated combinations of
operation names, for example, cascade="persist,m erge,evict" or cascade="all,deleteorphan". See 「連鎖的な永続化」 for a full explanation. Note that single valued associations (many-toone and one-to-one associations) do not support orphan delete.
典型的な m any-to-one 宣言は次のようにシンプルです。:
<many-to-one name="product" class="Product" column="PRODUCT_ID"/>
T he property-ref attribute should only be used for mapping legacy data where a foreign key refers
to a unique key of the associated table other than the primary key. T his is an ugly relational model. For
example, suppose the Product class had a unique serial number, that is not the primary key. (T he
unique attribute controls Hibernate's DDL generation with the SchemaExport tool.)
<property name="serialNumber" unique="true" type="string"
column="SERIAL_NUMBER"/>
以下のように OrderItem に対してマッピングを使えます:
<many-to-one name="product" property-ref="serialNumber"
column="PRODUCT_SERIAL_NUMBER"/>
しかし、これは決して推奨できません。
If the referenced unique key comprises multiple properties of the associated entity, you should map the
referenced properties inside a named <properties> element.
もし参照したユニークキーがコンポーネントのプロパティである場合は、プロパティのパスを指定できま
す:
<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>
5.1.11. one-to-one
他の永続クラスへの一対一関連は、one-to-one 要素で定義します。
81
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<one-to-one
name="propertyName"
class="ClassName"
cascade="cascade_style"
constrained="true|false"
fetch="join|select"
property-ref="propertyNameFromAssociatedClass"
access="field|property|ClassName"
formula="any SQL expression"
lazy="proxy|no-proxy|false"
entity-name="EntityName"
node="element-name|@attribute-name|element/@attribute|."
embed-xml="true|false"
foreign-key="foreign_key_name"
/>
nam e:プロパティ名。
class(オプション - デフォルトはリフレクションにより決定されるプロパティの型): 関連クラスの名
前。
cascade(オプション): 親オブジェクトから関連オブジェクトへ、どの操作をカスケードするかを指
定します。
constrained(オプション): マッピングされたテーブルの主キーに対する外部キー制約が、関連クラ
スのテーブルを参照することを指定します。このオプションは save() と delete() がカスケードされ
る順序に影響し、そして関連がプロキシされるかどうかにも影響します (そしてスキーマエクスポート
ツールにも使われます)。
fetch(オプション - デフォルトは select ): 外部結合フェッチと順次選択フェッチ(sequential
select fetch)のどちらかを選択します。
property-ref(オプション): このクラスの主キーに結合された関連クラスのプロパティ名。指定さ
れなければ、関連クラスの主キーが使われます。
access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用
すべき戦略。
form ula (オプション): ほとんどすべての一対一関連はオーナーのエンティティの主キーへとマッピング
されます。これ以外の稀な場合は、他のカラムや、複数のカラム、 SQL 構文を使った結合するための式
を指定できます。(例は org.hibernate.test.onetooneform ula を参照してください。)
lazy (optional - defaults to proxy): By default, single point associations are proxied. lazy="noproxy" specifies that the property should be fetched lazily when the instance variable is first accessed
(requires build-time bytecode instrumentation). lazy="false" specifies that the association will
always be eagerly fetched. Note that if constrained="false", proxying is impossible and Hibernate will
eager fetch the association!
entity-nam e (オプション):関連したクラスのエンティティ名。
一対一関連には2種類あります:
主キー関連
ユニーク外部キー関連
Primary key associations don't need an extra table column; if two rows are related by the association
82
第5章 基本的な O/R マッピング
then the two table rows share the same primary key value. So if you want two objects to be related by a
primary key association, you must make sure that they are assigned the same identifier value!
主キー関連を行うためには、以下のマッピングを Em ployee と Person のそれぞれに追加してくださ
い。
<one-to-one name="person" class="Person"/>
<one-to-one name="employee" class="Employee" constrained="true"/>
ここで、 PERSON と EMPLOYEE テーブルの関係する行の主キーが同じであることを確実にしなければ
いけません。ここでは、 foreign という特殊な Hibernate 識別子生成戦略を使います:
<class name="person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="foreign">
<param name="property">employee</param>
</generator>
</id>
...
<one-to-one name="employee"
class="Employee"
constrained="true"/>
</class>
Em ployee インスタンスが、 Person の em ployee プロパティで参照されるように、新しくセーブさ
れた Person のインスタンスには同じ主キーの値が代入されます。新しくセーブする Person インスタ
ンスは、その Person の em ployee プロパティが参照する Em ployee インスタンスとして同じ主キー
が割り当てられます。
もう1つの方法として、 Em ployee から Person へのユニーク制約を使った外部キー関連は以下のよう
に表現されます:
<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>
そしてこの関連は、以下の記述を Person のマッピングに追加することで双方向にすることができます:
<one-to-one name"employee" class="Employee" property-ref="person"/>
5.1.12. natural-id
<natural-id mutable="true|false"/>
<property ... />
<many-to-one ... />
......
</natural-id>
Even though we recommend the use of surrogate keys as primary keys, you should still try to identify
natural keys for all entities. A natural key is a property or combination of properties that is unique and
non-null. If it is also immutable, even better. Map the properties of the natural key inside the <naturalid> element. Hibernate will generate the necessary unique key and nullability constraints, and your
mapping will be more self-documenting.
エンティティの自然キープロパティの比較には、 equals() と hashCode() の実装を強くお勧めしま
83
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
す。
このマッピングは自然主キーを使ったエンティティでの使用を意図していません。
m utable (オプション、 デフォルトは false ): デフォルトでは、自然識別子プロパティは不変(定数)
と想定されています。
5.1.13. component, dynamic-component
T he <com ponent> element maps properties of a child object to columns of the table of a parent class.
Components may, in turn, declare their own properties, components or collections. See "Components"
below.
<component
name="propertyName"
class="className"
insert="true|false"
update="true|false"
access="field|property|ClassName"
lazy="true|false"
optimistic-lock="true|false"
unique="true|false"
node="element-name|."
>
<property ...../>
<many-to-one .... />
........
</component>
nam e:プロパティ名。
class (オプション - デフォルトはリフレクションにより決定されるプロパティの型): コンポーネン
ト(子)クラスの名前。
insert:マッピングされたカラムが SQL の INSERT に現れるようにするかどうかを指定します。
update:マッピングされたカラムが SQL の UPDAT E に現れるようにするかどうかを指定します。
access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用
すべき戦略。
lazy (オプション - デフォルトは false ): インスタンス変数に最初にアクセスしたときに、コンポーネ
ントを遅延してフェッチするよう指定します。 (バイトコード実装を作成する時間が必要になります)
optim istic-lock (オプション - デフォルトは true ): このプロパティの更新に、楽観ロックの取得を
要求するかどうかを指定します。言い換えれば、このプロパティがダーティであるときにバージョンを増
やすべきかを決定します。
unique (オプション - デフォルトは false ): コンポーネントのすべてのマッピングするカラムに、ユ
ニーク制約が存在するかを指定します。
T he child <property> tags map properties of the child class to table columns.
T he <com ponent> element allows a <parent> subelement that maps a property of the component
class as a reference back to the containing entity.
84
第5章 基本的な O/R マッピング
T he <dynam ic-com ponent> element allows a Map to be mapped as a component, where the
property names refer to keys of the map, see 「動的コンポーネント」.
5.1.14. properties
T he <properties> element allows the definition of a named, logical grouping of properties of a class.
T he most important use of the construct is that it allows a combination of properties to be the target of a
property-ref. It is also a convenient way to define a multi-column unique constraint.
<properties
name="logicalName"
insert="true|false"
update="true|false"
optimistic-lock="true|false"
unique="true|false"
>
<property ...../>
<many-to-one .... />
........
</properties>
nam e : グルーピングの論理名。実際のプロパティ名では ありません 。
insert:マッピングされたカラムが SQL の INSERT に現れるようにするかどうかを指定します。
update:マッピングされたカラムが SQL の UPDAT E に現れるようにするかどうかを指定します。
optim istic-lock (オプション - デフォルトは true ): これらのプロパティの更新に楽観的ロックの取
得を要求するかどうかを指定します。言い換えれば、このプロパティがダーティであるときにバージョン
を増やすべきかを決定します。
unique (オプション - デフォルトは false ): コンポーネントのすべてのマッピングするカラムに、ユ
ニーク制約が存在するかを指定します。
For example, if we have the following <properties> mapping:
<class name="Person">
<id name="personNumber"/>
...
<properties name="name"
unique="true" update="false">
<property name="firstName"/>
<property name="initial"/>
<property name="lastName"/>
</properties>
</class>
主キーの代わりに Person テーブルのユニークキーへの参照を持つ、レガシーデータの関連を持つかもし
れません。:
85
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
We don't recommend the use of this kind of thing outside the context of mapping legacy data.
5.1.15. subclass
Finally, polymorphic persistence requires the declaration of each subclass of the root persistent class.
For the table-per-class-hierarchy mapping strategy, the <subclass> declaration is used.
<subclass
name="ClassName"
discriminator-value="discriminator_value"
proxy="ProxyInterface"
lazy="true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
entity-name="EntityName"
node="element-name"
extends="SuperclassName">
<property .... />
.....
</subclass>
nam e:サブクラスの完全修飾されたクラス名。
discrim inator-value(オプション - デフォルトはクラス名): 個々のサブクラスを区別するための
値。
proxy (オプション): 遅延初期化プロキシに使用するクラスやインターフェースを指定します。
lazy (optional, defaults to true): Setting lazy="false" disables the use of lazy fetching.
Each subclass should declare its own persistent properties and subclasses. <version> and <id>
properties are assumed to be inherited from the root class. Each subclass in a heirarchy must define a
unique discrim inator-value. If none is specified, the fully qualified Java class name is used.
For information about inheritance mappings, see 9章継承マッピング.
5.1.16. joined-subclass
Alternatively, each subclass may be mapped to its own table (table-per-subclass mapping strategy).
Inherited state is retrieved by joining with the table of the superclass. We use the <joinedsubclass> element.
86
第5章 基本的な O/R マッピング
<joined-subclass
name="ClassName"
table="tablename"
proxy="ProxyInterface"
lazy="true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
schema="schema"
catalog="catalog"
extends="SuperclassName"
persister="ClassName"
subselect="SQL expression"
entity-name="EntityName"
node="element-name">
<key .... >
<property .... />
.....
</joined-subclass>
nam e:サブクラスの完全修飾されたクラス名。
table :サブクラステーブルの名前。
proxy (オプション): 遅延初期化プロキシに使用するクラスやインターフェースを指定します。
lazy (optional, defaults to true): Setting lazy="false" disables the use of lazy fetching.
No discriminator column is required for this mapping strategy. Each subclass must, however, declare a
table column holding the object identifier using the <key> element. T he mapping at the start of the
chapter would be re-written as:
87
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Cat" table="CATS">
<id name="id" column="uid" type="long">
<generator class="hilo"/>
</id>
<property name="birthdate" type="date"/>
<property name="color" not-null="true"/>
<property name="sex" not-null="true"/>
<property name="weight"/>
<many-to-one name="mate"/>
<set name="kittens">
<key column="MOTHER"/>
<one-to-many class="Cat"/>
</set>
<joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
<key column="CAT"/>
<property name="name" type="string"/>
</joined-subclass>
</class>
<class name="eg.Dog">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping>
For information about inheritance mappings, see 9章継承マッピング.
5.1.17. union-subclass
A third option is to map only the concrete classes of an inheritance hierarchy to tables, (the table-perconcrete-class strategy) where each table defines all persistent state of the class, including inherited
state. In Hibernate, it is not absolutely necessary to explicitly map such inheritance hierarchies. You can
simply map each class with a separate <class> declaration. However, if you wish use polymorphic
associations (e.g. an association to the superclass of your hierarchy), you need to use the <unionsubclass> mapping.
88
第5章 基本的な O/R マッピング
<union-subclass
name="ClassName"
table="tablename"
proxy="ProxyInterface"
lazy="true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
schema="schema"
catalog="catalog"
extends="SuperclassName"
abstract="true|false"
persister="ClassName"
subselect="SQL expression"
entity-name="EntityName"
node="element-name">
<property .... />
.....
</union-subclass>
nam e:サブクラスの完全修飾されたクラス名。
table :サブクラステーブルの名前。
proxy (オプション): 遅延初期化プロキシに使用するクラスやインターフェースを指定します。
lazy (optional, defaults to true): Setting lazy="false" disables the use of lazy fetching.
このマッピング戦略では識別カラムやキーカラムは必要ありません。
For information about inheritance mappings, see 9章継承マッピング.
5.1.18. join
Using the <join> element, it is possible to map properties of one class to several tables, when there's
a 1-to-1 relationship between the tables.
<join
table="tablename"
schema="owner"
catalog="catalog"
fetch="join|select"
inverse="true|false"
optional="true|false">
<key ... />
<property ... />
...
</join>
table :結合したテーブルの名前
schem a (optional): Override the schema name specified by the root <hibernate-m apping> element.
catalog (optional): Override the catalog name specified by the root <hibernate-m apping>
element.
89
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
fetch (optional - defaults to join): If set to join, the default, Hibernate will use an inner join to
retrieve a <join> defined by a class or its superclasses and an outer join for a <join> defined by a
subclass. If set to select then Hibernate will use a sequential select for a <join> defined on a
subclass, which will be issued only if a row turns out to represent an instance of the subclass. Inner
joins will still be used to retrieve a <join> defined by the class and its superclasses.
inverse (オプション - デフォルトは false ): もし可能であれば、 Hibernate はこの結合で定義されて
いるプロパティに対し挿入や更新を行いません。
optional (オプション - デフォルトは false ): もし可能であれば、 Hibernate はこの結合で定義された
プロパティが null でない場合にのみ行を挿入し、そのプロパティの検索には常に外部結合を使用します。
例えば人のアドレスの情報を分離したテーブルにマッピングすることが可能です (すべてのプロパティに
対して値型のセマンティクスを保持します):
<class name="Person"
table="PERSON">
<id name="id" column="PERSON_ID">...</id>
<join table="ADDRESS">
<key column="ADDRESS_ID"/>
<property name="address"/>
<property name="zip"/>
<property name="country"/>
</join>
...
この特徴はしばしばレガシーデータモデルに対してのみ有用ですが、クラスよりも少ないテーブルと、き
めの細かいドメインモデルを推奨します。しかし後で説明するように、1つのクラス階層で継承のマッピ
ング戦略を切り替える時には有用です。
5.1.19. key
We've seen the <key> element crop up a few times now. It appears anywhere the parent mapping
element defines a join to a new table, and defines the foreign key in the joined table, that references the
primary key of the original table.
<key
column="columnname"
on-delete="noaction|cascade"
property-ref="propertyName"
not-null="true|false"
update="true|false"
unique="true|false"
/>
colum n (optional): T he name of the foreign key column. T his may also be specified by nested
<colum n> element(s).
on-delete (オプション, デフォルトは noaction): 外部キー制約がデータベースレベルでカスケード削
除が可能かどうかを指定します。
property-ref (オプション): オリジナルテーブルの主キーではないカラムを参照する外部キーを指定し
ます (レガシーデータに対して提供されます)。
90
第5章 基本的な O/R マッピング
not-null (オプション): 外部キーカラムが null 値を許容しないことを指定します (このことは外部キーが
主キーの一部であることを暗黙的に示します)。
update (オプション): 外部キーを決して更新してはならないことを指定します (このことは外部キーが主
キーの一部であることを暗黙的に示します)。
unique (オプション): 外部キーがユニーク制約を持つべきであることを指定します (このことは外部キー
が主キーの一部であることを暗黙的に示します)。
We recommend that for systems where delete performance is important, all keys should be defined ondelete="cascade", and Hibernate will use a database-level ON CASCADE DELET E constraint,
instead of many individual DELET E statements. Be aware that this feature bypasses Hibernate's usual
optimistic locking strategy for versioned data.
T he not-null and update attributes are useful when mapping a unidirectional one to many
association. If you map a unidirectional one to many to a non-nullable foreign key, you must declare the
key column using <key not-null="true">.
5.1.20. column と formula 要素
Any mapping element which accepts a colum n attribute will alternatively accept a <colum n>
subelement. Likewise, <form ula> is an alternative to the form ula attribute.
<column
name="column_name"
length="N"
precision="N"
scale="N"
not-null="true|false"
unique="true|false"
unique-key="multicolumn_unique_key_name"
index="index_name"
sql-type="sql_type_name"
check="SQL expression"
default="SQL expression"/>
<formula>SQL expression</formula>
同じプロパティや関連のマッピングの中で、 colum n と form ula 属性を組み合わせることができま
す。例えば、特殊な結合条件などです。
<many-to-one name="homeAddress" class="Address"
insert="false" update="false">
<column name="person_id" not-null="true" length="10"/>
<formula>'MAILING'</formula>
</many-to-one>
5.1.21. import
Suppose your application has two persistent classes with the same name, and you don't want to specify
the fully qualified (package) name in Hibernate queries. Classes may be "imported" explicitly, rather than
relying upon auto-im port="true". You may even import classes and interfaces that are not explicitly
mapped.
<import class="java.lang.Object" rename="Universe"/>
91
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<import
class="ClassName"
rename="ShortName"
/>
class: Java クラスの完全修飾されたクラス名。
renam e (オプション - デフォルトは修飾されていないクラス名): クエリ言語で使われる名前。
5.1.22. any
T here is one further type of property mapping. T he <any> mapping element defines a polymorphic
association to classes from multiple tables. T his type of mapping always requires more than one column.
T he first column holds the type of the associated entity. T he remaining columns hold the identifier. It is
impossible to specify a foreign key constraint for this kind of association, so this is most certainly not
meant as the usual way of mapping (polymorphic) associations. You should use this only in very special
cases (eg. audit logs, user session data, etc).
m eta-type により、アプリケーションはカスタム型を指定できます。このカスタム型はデータベースカ
ラムの値を、 id-type で指定した型の識別子プロパティを持った永続クラスへマッピングします。
meta-type の値からクラス名へのマッピングを指定しなければなりません。
<any name="being" id-type="long" meta-type="string">
<meta-value value="TBL_ANIMAL" class="Animal"/>
<meta-value value="TBL_HUMAN" class="Human"/>
<meta-value value="TBL_ALIEN" class="Alien"/>
<column name="table_name"/>
<column name="id"/>
</any>
<any
name="propertyName"
id-type="idtypename"
meta-type="metatypename"
cascade="cascade_style"
access="field|property|ClassName"
optimistic-lock="true|false"
>
<meta-value ... />
<meta-value ... />
.....
<column .... />
<column .... />
.....
</any>
nam e: プロパティ名。
id-type: 識別子の型。
m eta-type(オプション - デフォルトは string ): ディスクリミネータマッピングで許された型。
cascade(オプション - デフォルトは none ): カスケードのスタイル。
access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用
すべき戦略。
92
第5章 基本的な O/R マッピング
optim istic-lock (オプション - デフォルトは true ): このプロパティの更新に楽観ロックの取得を要
求するかどうかを指定します。言い換えれば、このプロパティがダーティであるときにバージョンを増や
すべきかを定義します。
5.2. Hibernate の 型
5.2.1. エンティティと値
永続サービスに関わる様々な Java 言語レベルのオブジェクトの振る舞いを理解するためには、オブジェ
クトを2つのグループに分ける必要があります:
エンティティ はエンティティへの参照を保持する、他のすべてのオブジェクトから独立して存在します。
参照されないオブジェクトがガベージコレクトされてしまう性質を持つ通常の Java モデルと、これを比
べてみてください。(親エンティティから子へ、セーブと削除が カスケード されうることを除いて)エ
ンティティは明示的にセーブまたは削除されなければなりません。これは到達可能性によるオブジェクト
永続化の ODMG モデルとは異なっています。大規模なシステムでアプリケーションオブジェクトが普通
どのように使われるかにより密接に対応します。エンティティは循環と参照の共有をサポートします。ま
たそれらはバージョン付けすることもできます。
An entity's persistent state consists of references to other entities and instances of value types. Values
are primitives, collections (not what's inside a collection), components and certain immutable objects.
Unlike entities, values (in particular collections and components) are persisted and deleted by
reachability. Since value objects (and primitives) are persisted and deleted along with their containing
entity they may not be independently versioned. Values have no independent identity, so they cannot be
shared by two entities or collections.
Up until now, we've been using the term "persistent class" to refer to entities. We will continue to do that.
Strictly speaking, however, not all user-defined classes with persistent state are entities. A component is
a user defined class with value semantics. A Java property of type java.lang.String also has value
semantics. Given this definition, we can say that all types (classes) provided by the JDK have value type
semantics in Java, while user-defined types may be mapped with entity or value type semantics. T his
decision is up to the application developer. A good hint for an entity class in a domain model are shared
references to a single instance of that class, while composition or aggregation usually translates to a
value type.
We'll revisit both concepts throughout the documentation.
T he challenge is to map the Java type system (and the developers' definition of entities and value types)
to the SQL/database type system. T he bridge between both systems is provided by Hibernate: for
entities we use <class>, <subclass> and so on. For value types we use <property>,
<com ponent>, etc, usually with a type attribute. T he value of this attribute is the name of a Hibernate
mapping type. Hibernate provides many mappings (for standard JDK value types) out of the box. You
can write your own mapping types and implement your custom conversion strategies as well, as you'll
see later.
コレクションを除く組み込みの Hibernate の型はすべて、 null セマンティクスをサポートします。
5.2.2. 基本的な型
組み込みの 基本的なマッピング型 は大まかに以下のように分けられます。
integer, long, short, float, double, character, byte, boolean, yes_no,
true_false
Java のプリミティブやラッパークラスから適切な(ベンダー固有の) SQL カラム型への型マッ
ピング。 boolean, yes_no と true_false は、すべて Java の boolean または
93
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
java.lang.Boolean の代替エンコードです。
string
java.lang.String から VARCHAR (または Oracle の VARCHAR2 )への型マッピング。
date, tim e, tim estam p
java.util.Date とそのサブクラスから SQL 型の DAT E 、 T IME 、 T IMEST AMP (またはそ
れらと等価なもの) への型マッピング。
calendar, calendar_date
java.util.Calendar から SQL 型 の「 T IMEST AMP 、 DAT E (またはそれらと等価なもの)
への型マッピング。
big_decim al, big_integer
java.m ath.BigDecim al と java.m ath.BigInteger から NUMERIC(または Oracle の
NUMBER )への型マッピング。
locale, tim ezone, currency
java.util.Locale 、 java.util.T im eZone 、 java.util.Currency から VARCHAR
(または Oracle の VARCHAR2 )への型マッピング。 Locale と Currency のインスタンス
は、それらの ISO コードにマッピングされます。 T im eZone のインスタンスは、それらの ID
にマッピングされます。
class
java.lang.Class から VARCHAR (または Oracle の VARCHAR2 )への型マッピング。
Class はその完全修飾された名前にマッピングされます。
binary
バイト配列は、適切な SQL のバイナリ型にマッピングされます。
text
長い Java 文字列は、 SQL の CLOB または T EXT 型にマッピングされます。
serializable
シリアライズ可能な Java 型は、適切な SQL のバイナリ型にマッピングされます。デフォルトで
基本型ではないシリアライズ可能な Java クラスやインターフェースの名前を指定することで、
Hibernate の型を serializable とすることもできます。
clob, blob
JDBC クラス java.sql.Clob と java.sql.Blob に対する型マッピング。 blob や clob オブ
ジェクトはトランザクションの外では再利用できないため、アプリケーションによっては不便か
もしれません。(さらにはドライバサポートが一貫していません。)
im m _date, im m _tim e, im m _tim estam p, im m _calendar, im m _calendar_date,
94
第5章 基本的な O/R マッピング
im m _serializable, im m _binary
ほとんどの場合に可変である Java の型に対する型マッピング。 Hibernate は不変な Java の型に
対しては最適化を行い、アプリケーションはそれを不変オブジェクトとして扱います。例えば
im m _tim estam p としてマップしたインスタンスに対して、 Date.setT im e() を呼び出して
はなりません。プロパティの値を変更しその変更を永続化するためには、アプリケーションはプ
ロパティに対して新しい (同一でない) オブジェクトを割り当てなければなりません。
エンティティとコレクションのユニークな識別子は、 binary 、 blob 、 clob を除く、どんな基本型
でも構いません。(複合識別子でも構いません。以下を見てください。)
基本的な値型には、 org.hibernate.Hibernate で定義された T ype 定数がそれぞれあります。例え
ば、 Hibernate.ST RING は string 型を表現しています。
5.2.3. カスタム型
開発者が独自の値型を作成することは、比較的簡単です。例えば、 java.lang.BigInteger 型のプロ
パティを VARCHAR カラムに永続化したいかもしれません。 Hibernate はこのための組み込み型を用意し
ていません。しかしカスタム型は、プロパティ(またはコレクションの要素)を1つのテーブルカラムに
マッピングするのに制限はありません。そのため例えば、 java.lang.String 型の getNam e() /
setNam e() Java プロパティを FIRST _NAME 、 INIT IAL 、 SURNAME カラムに永続化できます。
カスタム型を実装するには、 org.hibernate.UserT ype または
org.hibernate.Com positeUserT ype を実装し、型の完全修飾された名前を使ってプロパティを定
義します。どのような種類のものが可能かを調べるには、
org.hibernate.test.DoubleStringT ype を確認してください。
<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
<column name="first_string"/>
<column name="second_string"/>
</property>
Notice the use of <colum n> tags to map a property to multiple columns.
Com positeUserT ype 、 EnhancedUserT ype 、 UserCollectionT ype 、 UserVersionT ype
インターフェースは、より特殊な使用法に対してのサポートを提供します。
You may even supply parameters to a UserT ype in the mapping file. T o do this, your UserT ype must
implement the org.hibernate.usertype.Param eterizedT ype interface. T o supply parameters
to your custom type, you can use the <type> element in your mapping files.
<property name="priority">
<type name="com.mycompany.usertypes.DefaultValueIntegerType">
<param name="default">0</param>
</type>
</property>
UserT ype は、引数として渡された Properties オブジェクトから、 default で指定したパラメータ
に対する値を検索することができます。
If you use a certain UserT ype very often, it may be useful to define a shorter name for it. You can do
this using the <typedef> element. T ypedefs assign a name to a custom type, and may also contain a
list of default parameter values if the type is parameterized.
95
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<typedef class="com.mycompany.usertypes.DefaultValueIntegerType"
name="default_zero">
<param name="default">0</param>
</typedef>
<property name="priority" type="default_zero"/>
プロパティのマッピングで型パラメータを使うことで、 typedef で提供されたパラメータをその都度オー
バーライドすることが可能です。
Even though Hibernate's rich range of built-in types and support for components means you will very
rarely need to use a custom type, it is nevertheless considered good form to use custom types for (nonentity) classes that occur frequently in your application. For example, a MonetaryAm ount class is a
good candidate for a Com positeUserT ype, even though it could easily be mapped as a component.
One motivation for this is abstraction. With a custom type, your mapping documents would be futureproofed against possible changes in your way of representing monetary values.
5.3. 1 つ の ク ラ ス に 1 つ 以 上 の マ ッ ピ ン グ
ある永続クラスに、一つ以上のマッピングを提供することが出来ます。この場合、マッピングする2つの
エンティティのインスタンスを明確にするために、 エンティティ名 を指定しなければなりません (デフォ
ルトではエンティティ名はクラス名と同じです。)。 Hibernate では、永続オブジェクトを扱うとき、クエ
リを書き込むとき、指定されたエンティティへの関連をマッピングするときに、エンティティ名を指定し
なければなりません。
<class name="Contract" table="Contracts"
entity-name="CurrentContract">
...
<set name="history" inverse="true"
order-by="effectiveEndDate desc">
<key column="currentContractId"/>
<one-to-many entity-name="HistoricalContract"/>
</set>
</class>
<class name="Contract" table="ContractHistory"
entity-name="HistoricalContract">
...
<many-to-one name="currentContract"
column="currentContractId"
entity-name="CurrentContract"/>
</class>
関連が class の代わりに entity-nam e を使って、どのように指定されるのかに注目してください。
5.4. バ ッ ク ク ォ ー ト で 囲 ん だ SQL 識 別 子
マッピングドキュメントでテーブルやカラムの名前をバッククォートで囲むことで、 Hibernate で生成さ
れた SQL 中の識別子を引用させることができます。 Hibernate は SQL の Dialect に対応する、正しい
引用スタイルを使います(普通はダブルクォートですが、 SQL Server ではかぎ括弧、 MySQL ではバッ
ククォートです)。
96
第5章 基本的な O/R マッピング
<class name="LineItem" table="`Line Item`">
<id name="id" column="`Item Id`"/><generator class="assigned"/></id>
<property name="itemNumber" column="`Item #`"/>
...
</class>
5.5. メ タ デ ー タ の 代 替 手 段
XML isn't for everyone, and so there are some alternative ways to define O/R mapping metadata in
Hibernate.
5.5.1. XDoclet マークアップの使用
多くの Hibernate ユーザーは XDoclet の @ hibernate.tags を使って、ソースコード内に直接マッピン
グ情報を埋め込むことを好みます。これは厳密に言えば XDoclet の分野なので、本ドキュメントではこの
方法を対象とはしません。しかし XDoclet を使った以下の Cat マッピングの例を示します。
97
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
package eg;
import java.util.Set;
import java.util.Date;
/**
* @hibernate.class
* table="CATS"
*/
public class Cat {
private Long id; // identifier
private Date birthdate;
private Cat mother;
private Set kittens
private Color color;
private char sex;
private float weight;
/*
* @hibernate.id
* generator-class="native"
* column="CAT_ID"
*/
public Long getId() {
return id;
}
private void setId(Long id) {
this.id=id;
}
/**
* @hibernate.many-to-one
* column="PARENT_ID"
*/
public Cat getMother() {
return mother;
}
void setMother(Cat mother) {
this.mother = mother;
}
/**
* @hibernate.property
* column="BIRTH_DATE"
*/
public Date getBirthdate() {
return birthdate;
}
void setBirthdate(Date date) {
birthdate = date;
}
/**
* @hibernate.property
* column="WEIGHT"
*/
public float getWeight() {
return weight;
}
void setWeight(float weight) {
this.weight = weight;
}
98
第5章 基本的な O/R マッピング
/**
* @hibernate.property
* column="COLOR"
* not-null="true"
*/
public Color getColor() {
return color;
}
void setColor(Color color) {
this.color = color;
}
/**
* @hibernate.set
* inverse="true"
* order-by="BIRTH_DATE"
* @hibernate.collection-key
* column="PARENT_ID"
* @hibernate.collection-one-to-many
*/
public Set getKittens() {
return kittens;
}
void setKittens(Set kittens) {
this.kittens = kittens;
}
// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
kittens.add(kitten);
}
/**
* @hibernate.property
* column="SEX"
* not-null="true"
* update="false"
*/
public char getSex() {
return sex;
}
void setSex(char sex) {
this.sex=sex;
}
}
Hibernate のウェブサイトには、 XDoclet と Hibernate に関するサンプルが多数あります。
5.5.2. JDK 5.0 アノテーションの使用
JDK5.0 ではタイプセーフかつコンパイル時にチェックできる、言語レベルの XDoclet スタイルのアノ
テーションを導入しました。このメカニズムは XDoclet のアノテーションよりも強力で、ツールや IDE も
多くがサポートしています。例えば IntelliJ IDEA は、 JDK5.0 にアノテーションの自動補完と構文の強調
表示をサポートしています。 EJB 仕様 (JSR-220) の新しいバージョンでは、エンティティ Bean に対す
る主要なメタデータメカニズムとして JDK5.0 のアノテーションを使用しています。 Hibernate3 では
JSR-220 (永続化 API) の EntityManager を実装し、メタデータマッピングに対するサポートは、別ダ
ウンロードの Hibernate Annotations パッケージにより利用可能です。これは EJB3 (JSR-220) と
Hibernate3 のメタデータをどちらもサポートしています。
以下は EJB のエンティティ Bean として注釈された POJO クラスの例です:
99
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
@Entity(access = AccessType.FIELD)
public class Customer implements Serializable {
@Id;
Long id;
String firstName;
String lastName;
Date birthday;
@Transient
Integer age;
@Embedded
private Address homeAddress;
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="CUSTOMER_ID")
Set<Order> orders;
// Getter/setter and business methods
}
JDK5.0 のアノテーション (と JSR-220) のサポートは進行中の作業であり、完全ではないことに注意して
ください。さらに詳しい情報は Hibernate のアノテーションモジュールを参照してください。
5.6. 生 成 プ ロ パ テ ィ
生成プロパティとは、データベースによって生成された値を持つプロパティです。通常、 Hibernate アプ
リケーションは、データベースが値を生成したプロパティを含むオブジェクトを リフレッシュ する必要
がありました。しかし、プロパティが生成されたということをマークすることで、アプリケーションはリ
フレッシュの責任を Hibernate に委譲します。基本的に、生成プロパティを持つと定義したエンティティ
に対して Hibernate が INSERT や UPDAT E の SQL を発行した後すぐに、生成された値を読み込むための
SELECT SQL が発行されます。
Properties marked as generated must additionally be non-insertable and non-updateable. Only
「version(オプション)」 versions, 「timestamp(オプション)」 timestamps, and 「property」
simple properties can be marked as generated.
never (デフォルト) - 与えられたプロパティの値は、データベースから生成されないことを意味します。
insert - states that the given property value is generated on insert, but is not regenerated on
subsequent updates. T hings like created-date would fall into this category. Note that even thought
「version(オプション)」 version and 「timestamp(オプション)」 timestamp properties can be
marked as generated, this option is not available there...
always - 挿入時も更新時もプロパティの値が生成されることを示します。
5.7. 補 助 的 な デ ー タ ベ ー ス オ ブ ジ ェ ク ト
Allows CREAT E and DROP of arbitrary database objects, in conjunction with Hibernate's schema
evolution tools, to provide the ability to fully define a user schema within the Hibernate mapping files.
Although designed specifically for creating and dropping things like triggers or stored procedures, really
any SQL command that can be run via a java.sql.Statem ent.execute() method is valid here
(ALT ERs, INSERT S, etc). T here are essentially two modes for defining auxiliary database objects...
100
第5章 基本的な O/R マッピング
1つ目の方法は、 CREAT E と DROP コマンドをマッピングファイルの外に、明示的に記載することです:
<hibernate-mapping>
...
<database-object>
<create>CREATE TRIGGER my_trigger ...</create>
<drop>DROP TRIGGER my_trigger</drop>
</database-object>
</hibernate-mapping>
2つ目の方法は、 CREAT E と DROP コマンドの組み立て方を知っているカスタムクラスを提供すること
です。このカスタムクラスは org.hibernate.m apping.AuxiliaryDatabaseObject インタ
フェースを実装しなければなりません。
<hibernate-mapping>
...
<database-object>
<definition class="MyTriggerDefinition"/>
</database-object>
</hibernate-mapping>
さらに、あるデータベース方言が使用される時にだけ適用するといったように、データベースオブジェク
トが使われるケースを限定できます。
<hibernate-mapping>
...
<database-object>
<definition class="MyTriggerDefinition"/>
<dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
<dialect-scope name="org.hibernate.dialect.OracleDialect"/>
</database-object>
</hibernate-mapping>
101
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 6章 コレクションのマッピング
6.1. コ レ ク シ ョ ン の 永 続 化
コレクション型のフィールドを永続化するには、そのコレクション型がインターフェース型である必要が
あります。例えば、
public class Product {
private String serialNumber;
private Set parts = new HashSet();
public Set getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
}
T he actual interface might be java.util.Set, java.util.Collection, java.util.List,
java.util.Map, java.util.SortedSet, java.util.SortedMap or ... anything you like! (Where
"anything you like" means you will have to write an implementation of
org.hibernate.usertype.UserCollectionT ype.)
Notice how we initialized the instance variable with an instance of HashSet. T his is the best way to
initialize collection valued properties of newly instantiated (non-persistent) instances. When you make
the instance persistent - by calling persist(), for example - Hibernate will actually replace the
HashSet with an instance of Hibernate's own implementation of Set. Watch out for errors like this:
Cat cat = new DomesticCat();
Cat kitten = new DomesticCat();
....
Set kittens = new HashSet();
kittens.add(kitten);
cat.setKittens(kittens);
session.persist(cat);
kittens = cat.getKittens(); // Okay, kittens collection is a Set
(HashSet) cat.getKittens(); // Error!
Hibernate により注入された永続性コレクションは、インターフェース型に応じて、 HashMap や
HashSet、 T reeMap、 T reeSet、 ArrayList のように振舞います。
コレクションインスタンスは、値型として普通に振舞います。永続化オブジェクトに参照されたときに自
動的に永続化され、参照がなくなったときに自動的に削除されます。もしある永続化オブジェクトから別
の永続化オブジェクトに渡されたら、その要素は現在のテーブルから別のテーブルに移動するかもしれま
せん。2つのエンティティが同じコレクションインスタンスを共有してはいけません。リレーショナルモ
デルをベースにしているため、コレクション型のプロパティに null 値を代入しても意味がありません。つ
まり Hibernate は参照先のないコレクションと空のコレクションを区別しません。
You shouldn't have to worry much about any of this. Use persistent collections the same way you use
ordinary Java collections. Just make sure you understand the semantics of bidirectional associations
(discussed later).
6.2. コ レ ク シ ョ ン の マ ッ ピ ン グ
T he Hibernate mapping element used for mapping a collection depends upon the type of the interface.
102
第6章 コレクションのマッピング
For example, a <set> element is used for mapping properties of type Set.
<class name="Product">
<id name="serialNumber" column="productSerialNumber"/>
<set name="parts">
<key column="productSerialNumber" not-null="true"/>
<one-to-many class="Part"/>
</set>
</class>
Apart from <set>, there is also <list>, <m ap>, <bag>, <array> and <prim itive-array>
mapping elements. T he <m ap> element is representative:
<map
name="propertyName"
table="table_name"
schema="schema_name"
lazy="true|extra|false"
inverse="true|false"
cascade="all|none|save-update|delete|all-delete-orphan|delete-orphan"
sort="unsorted|natural|comparatorClass"
order-by="column_name asc|desc"
where="arbitrary sql where condition"
fetch="join|select|subselect"
batch-size="N"
access="field|property|ClassName"
optimistic-lock="true|false"
mutable="true|false"
node="element-name|."
embed-xml="true|false"
>
<key .... />
<map-key .... />
<element .... />
</map>
nam e コレクション型であるプロパティの名前
table (オプション - デフォルトはプロパティ名)コレクションテーブルの名前(一対多関連では使用し
ません)。
schem a (オプション)テーブルスキーマの名前。ルート要素で宣言されているスキーマより優先されま
す。
lazy (optional - defaults to true) may be used to disable lazy fetching and specify that the association
is always eagerly fetched, or to enable "extra-lazy" fetching where most operations do not initialize the
collection (suitable for very large collections)
inverse (optional - defaults to false) mark this collection as the "inverse" end of a bidirectional
association
cascade (オプション - デフォルトは none) 子エンティティへのカスケード操作を有効にします。
sort (オプション)コレクションを自然な順序でソートする場合は natural を指定します。あるいは
Comparator クラスを指定します。
103
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
order-by (オプション、 JDK1.4 のみ) Map、 Set、 bag のイテレーション順序を定義するテーブル
カラムを指定すると共に、オプションとして asc、 desc を指定します。
where (オプション)コレクションの検索や削除の際に使う任意の SQL のWHERE 条件を指定します
(利用可能なデータの一部分だけをコレクションが含むべきときに、これは有用です)。
fetch (オプション - デフォルトは select) 外部結合によるフェッチ、順次選択フェッチ
(sequential select fetch) 、順次サブセレクトフェッチ (sequential subselect fetch) のどれかを選択
してください。
batch-size (optional, defaults to 1) specify a "batch size" for lazily fetching instances of this
collection.
access (オプション - デフォルトは property) コレクション型プロパティの値にアクセスするため
に使用する戦略です。
optim istic-lock (optional - defaults to true): Species that changes to the state of the collection
results in increment of the owning entity's version. (For one to many associations, it is often reasonable
to disable this setting.)
m utable(オプション - デフォルトは true) false 値は、コレクションの要素が変更されないことを
表します (ある場合には、少しパフォーマンスを高めます)。
6.2.1. コレクションの外部キー
Collection instances are distinguished in the database by the foreign key of the entity that owns the
collection. T his foreign key is referred to as the collection key column (or columns) of the collection table.
T he collection key column is mapped by the <key> element.
T here may be a nullability constraint on the foreign key column. For most collections, this is implied. For
unidirectional one to many associations, the foreign key column is nullable by default, so you might need
to specify not-null="true".
<key column="productSerialNumber" not-null="true"/>
外部キーの制約が ON DELET E CASCADE を使うかもしれません。
<key column="productSerialNumber" on-delete="cascade"/>
See the previous chapter for a full definition of the <key> element.
6.2.2. コレクションの要素
Collections may contain almost any other Hibernate type, including all basic types, custom types,
components, and of course, references to other entities. T his is an important distinction: an object in a
collection might be handled with "value" semantics (its lifecycle fully depends on the collection owner) or
it might be a reference to another entity, with its own lifecycle. In the latter case, only the "link" between
the two objects is considered to be state held by the collection.
T he contained type is referred to as the collection element type. Collection elements are mapped by
<elem ent> or <com posite-elem ent>, or in the case of entity references, with <one-to-m any> or
<m any-to-m any>. T he first two map elements with value semantics, the next two are used to map
entity associations.
6.2.3. インデックス付きのコレクション
104
第6章 コレクションのマッピング
All collection mappings, except those with set and bag semantics, need an index column in the collection
table - a column that maps to an array index, or List index, or Map key. T he index of a Map may be of
any basic type, mapped with <m ap-key>, it may be an entity reference mapped with <m ap-key-m anyto-m any>, or it may be a composite type, mapped with <com posite-m ap-key>. T he index of an
array or list is always of type integer and is mapped using the <list-index> element. T he mapped
column contains sequential integers (numbered from zero, by default).
<list-index
column="column_name"
base="0|1|..."/>
colum n_nam e (必須): コレクションインデックス値を持っているカラムの名前。
base (オプション - デフォルトは 0): リスト又は配列の1つめの要素に対応するインデックスカラムの
値。
<map-key
column="column_name"
formula="any SQL expression"
type="type_name"
node="@attribute-name"
length="N"/>
colum n (オプション): コレクションインデックス値を持っているカラムの名前。
form ula (オプション): マップのキーを評価するために使用される SQL 式。
class (必須): マップキーのタイプ
<map-key-many-to-many
column="column_name"
formula="any SQL expression"
class="ClassName"
/>
colum n (オプション): コレクションインデックス値のための外部キーカラムの名前。
form ula (オプション): マップキーの外部キーを評価するために使用される SQL 式。
class (必須): マップキーとして使用されるエンティティクラス。
If your table doesn't have an index column, and you still wish to use List as the property type, you
should map the property as a Hibernate <bag>. A bag does not retain its order when it is retrieved from
the database, but it may be optionally sorted or ordered.
多くの一般的なリレーショナルモデルをカバーしたために、コレクションのために利用できるマッピング
にはかなりの幅があります。様々なマッピング宣言がどのようにデータベーステーブルに変換されるかを
知るために、スキーマ生成ツールを使ってみると良いでしょう。
6.2.4. 値のコレクションと多対多関連
値のコレクションや多対多関連は、専用の コレクションテーブル が必要です。このテーブルは、外部
キーカラムと、 コレクション要素のカラム と、場合によってはインデックスカラムを持ちます。
For a collection of values, we use the <elem ent> tag.
105
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<element
column="column_name"
formula="any SQL expression"
type="typename"
length="L"
precision="P"
scale="S"
not-null="true|false"
unique="true|false"
node="element-name"
/>
colum n (オプション): コレクション要素の値を持っているカラムの名前。
form ula (オプション): 要素を評価するために使用される SQL 式。
type (必須): コレクション要素のタイプ。
A many-to-many association is specified using the <m any-to-m any> element.
<many-to-many
column="column_name"
formula="any SQL expression"
class="ClassName"
fetch="select|join"
unique="true|false"
not-found="ignore|exception"
entity-name="EntityName"
property-ref="propertyNameFromAssociatedClass"
node="element-name"
embed-xml="true|false"
/>
colum n (オプション): 要素の外部キーカラムの名前。
form ula (オプション): 要素の外部キーを評価するために使用される SQL 式。
class (必須): 関連クラスの名前。
fetch (optional - defaults to join): enables outer-join or sequential select fetching for this association.
T his is a special case; for full eager fetching (in a single SELECT ) of an entity and its many-to-many
relationships to other entities, you would enable join fetching not only of the collection itself, but also
with this attribute on the <m any-to-m any> nested element.
unique (オプション): 外部キーカラムのためのユニークな制約の DDL 生成を有効にします。これは関連
の多様性を効率的に一対多にします。
not-found (オプション - デフォルトは exception): 参照先の行がない外部キーをどのように扱うか
を指定します: ignore を指定すると、行がないことを関連がないものとして扱います。
entity-nam e (オプション): class の代替である関連クラスのエンティティ名。 class の代わりに
指定する、関連クラスのエンティティ名。
property-ref: (オプション) この外部キーに加わる、関連クラスのプロパティの名前。指定されていな
い場合は、関連クラスの主キーが使用されます。
以下にいくつか例を示します。まずは string の set に関しての例です。
106
第6章 コレクションのマッピング
<set name="names" table="person_names">
<key column="person_id"/>
<element column="person_name" type="string"/>
</set>
整数値を含む bag (bagは order-by 属性によって反復順序が定義されています):
<bag name="sizes"
table="item_sizes"
order-by="size asc">
<key column="item_id"/>
<element column="size" type="integer"/>
</bag>
エンティティの配列 - この場合、多対多の関連です。
<array name="addresses"
table="PersonAddress"
cascade="persist">
<key column="personId"/>
<list-index column="sortOrder"/>
<many-to-many column="addressId" class="Address"/>
</array>
文字列と日付の map
<map name="holidays"
table="holidays"
schema="dbo"
order-by="hol_name asc">
<key column="id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date" type="date"/>
</map>
コンポーネントの list (次の章で詳しく説明します)
<list name="carComponents"
table="CarComponents">
<key column="carId"/>
<list-index column="sortOrder"/>
<composite-element class="CarComponent">
<property name="price"/>
<property name="type"/>
<property name="serialNumber" column="serialNum"/>
</composite-element>
</list>
6.2.5. 一対多関連
一対多関連 は、コレクションテーブルを介さず、外部キーにより2つのクラスのテーブルを関連付けま
す。このマッピングは標準的な Java のコレクションのセマンティクスをいくつか失います:
エンティティクラスのインスタンスは、2つ以上のコレクションのインスタンスに属してはいけませ
ん。
107
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
コレクションに含まれるエンティティクラスのインスタンスは、コレクションインデックスの値とし
て2度以上現れてはいけません。
An association from Product to Part requires existence of a foreign key column and possibly an index
column to the Part table. A <one-to-m any> tag indicates that this is a one to many association.
<one-to-many
class="ClassName"
not-found="ignore|exception"
entity-name="EntityName"
node="element-name"
embed-xml="true|false"
/>
class (必須): 関連クラスの名前。
not-found (オプション - デフォルトは exception): 参照先の行がないキャッシュされた識別子をど
のように扱うかを指定します: ignore を指定すると、行がないことを関連がないものとして扱います。
entity-nam e (オプション): class の代替である関連クラスのエンティティ名。 class の代わりに
指定する、関連クラスのエンティティ名。
Notice that the <one-to-m any> element does not need to declare any columns. Nor is it necessary to
specify the table name anywhere.
Very important note: If the foreign key column of a <one-to-m any> association is declared NOT NULL,
you must declare the <key> mapping not-null="true" or use a bidirectional association with the
collection mapping marked inverse="true". See the discussion of bidirectional associations later in
this chapter.
次の例は、名称(Part の永続的なプロパティである partNam e) による Part エンティティの map を
表しています。 formula によるインデックスを使っていることに注意してください。
<map name="parts"
cascade="all">
<key column="productId" not-null="true"/>
<map-key formula="partName"/>
<one-to-many class="Part"/>
</map>
6.3. 高 度 な コ レ ク シ ョ ン マ ッ ピ ン グ
6.3.1. ソートされたコレクション
Hibernate は java.util.SortedMap と java.util.SortedSet を実装したコレクションをサポート
しています。開発者はマッピング定義ファイルにコンパレータを指定しなければなりません:
108
第6章 コレクションのマッピング
<set name="aliases"
table="person_aliases"
sort="natural">
<key column="person"/>
<element column="name" type="string"/>
</set>
<map name="holidays" sort="my.custom.HolidayComparator">
<key column="year_id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date" type="date"/>
</map>
sort 属性に設定できる値は unsorted と natural および、 java.util.Com parator を実装したク
ラスの名前です。
ソートされたコレクションは実質的には java.util.T reeSet や java.util.T reeMap のように振舞
います。
もしデータベース自身にコレクションの要素を並べさせたいなら、 set や bag、m ap の order-by 属性
を使います。この解決法は JDK1.4 、もしくはそれ以上のバージョンで利用可能です (LinkedHashSet
または LinkedHashMapを使って実装されています)。整列はメモリ上ではなく、 SQL クエリ内で実行
されます。
<set name="aliases" table="person_aliases" order-by="lower(name) asc">
<key column="person"/>
<element column="name" type="string"/>
</set>
<map name="holidays" order-by="hol_date, hol_name">
<key column="year_id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date type="date"/>
</map>
order-by 属性の値が SQL 命令であって、 HQL 命令ではないことに注意してください。
関連は、コレクションの filter() を使うことで、実行時に任意の criteria によってソートすることも可
能です。
sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();
6.3.2. 双方向関連
A bidirectional association allows navigation from both "ends" of the association. T wo kinds of
bidirectional association are supported:
one-to-many
片側が set か bag 、もう片方が単一値です。
many-to-many
両側が set か bag です。
2つの多対多関連で同じデータベーステーブルをマッピングし、片方を inverse として宣言することで、双
109
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
方向の多対多関連を指定することが出来ます (どちらを inverse に選んだとしても、そちら側にはイン
デックス付きのコレクションは使えません)。
Here's an example of a bidirectional many-to-many association; each category can have many items and
each item can be in many categories:
<class name="Category">
<id name="id" column="CATEGORY_ID"/>
...
<bag name="items" table="CATEGORY_ITEM">
<key column="CATEGORY_ID"/>
<many-to-many class="Item" column="ITEM_ID"/>
</bag>
</class>
<class name="Item">
<id name="id" column="CATEGORY_ID"/>
...
<!-- inverse end -->
<bag name="categories" table="CATEGORY_ITEM" inverse="true">
<key column="ITEM_ID"/>
<many-to-many class="Category" column="CATEGORY_ID"/>
</bag>
</class>
関連の inverse 側にのみ行われた変更は永続化 されません。これは、 Hibernate は全ての双方向関連につ
いて、メモリ上に2つの表現を持っているという意味です。つまり一つは A から B へのリンクで、もう一
つは B から A へのリンクということです。 Java のオブジェクトモデルについて考え、 Java で双方向関
係をどうやって作るかを考えれば、これは理解しやすいです。下記に、 Java での双方向関連を示しま
す。
category.getItems().add(item);
relationship
item.getCategories().add(category);
relationship
session.persist(item);
session.persist(category);
// The category now "knows" about the
// The item now "knows" about the
// The relationship won't be saved!
// The relationship will be saved
関連の inverse ではない側は、メモリ上の表現をデータベースに保存するのに使われます。
You may define a bidirectional one-to-many association by mapping a one-to-many association to the
same table column(s) as a many-to-one association and declaring the many-valued end
inverse="true".
110
第6章 コレクションのマッピング
<class name="Parent">
<id name="id" column="parent_id"/>
....
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<many-to-one name="parent"
class="Parent"
column="parent_id"
not-null="true"/>
</class>
Mapping one end of an association with inverse="true" doesn't affect the operation of cascades,
these are orthogonal concepts!
6.3.3. インデックス付きコレクションと双方向関連
A bidirectional association where one end is represented as a <list> or <m ap> requires special
consideration. If there is a property of the child class which maps to the index column, no problem, we
can continue using inverse="true" on the collection mapping:
<class name="Parent">
<id name="id" column="parent_id"/>
....
<map name="children" inverse="true">
<key column="parent_id"/>
<map-key column="name"
type="string"/>
<one-to-many class="Child"/>
</map>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<property name="name"
not-null="true"/>
<many-to-one name="parent"
class="Parent"
column="parent_id"
not-null="true"/>
</class>
But, if there is no such property on the child class, we can't think of the association as truly bidirectional
(there is information available at one end of the association that is not available at the other end). In this
case, we can't map the collection inverse="true". Instead, we could use the following mapping:
111
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="Parent">
<id name="id" column="parent_id"/>
....
<map name="children">
<key column="parent_id"
not-null="true"/>
<map-key column="name"
type="string"/>
<one-to-many class="Child"/>
</map>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<many-to-one name="parent"
class="Parent"
column="parent_id"
insert="false"
update="false"
not-null="true"/>
</class>
注意: このマッピングでは、関連のコレクション値の側は、外部キーをアップデートする責任がありま
す。 T ODO: これは本当にいくつかの不必要なアップデートステートメントをもたらすのでしょうか?
6.3.4. 3項関連
3項関連のマッピングには3つのアプローチがあります。1つ目は関連をインデックスとして Map を使用す
るアプローチです:
<map name="contracts">
<key column="employer_id" not-null="true"/>
<map-key-many-to-many column="employee_id" class="Employee"/>
<one-to-many class="Contract"/>
</map>
<map name="connections">
<key column="incoming_node_id"/>
<map-key-many-to-many column="outgoing_node_id" class="Node"/>
<many-to-many column="connection_id" class="Connection"/>
</map>
2つ目は単純に関連をエンティティクラスとしてモデルを作り直すアプローチで、頻繁に使われます。
最後は composite 要素を使うアプローチです。これに関する議論は後ほど行います。
6.3.5. Using an <idbag>
If you've fully embraced our view that composite keys are a bad thing and that entities should have
synthetic identifiers (surrogate keys), then you might find it a bit odd that the many to many associations
and collections of values that we've shown so far all map to tables with composite keys! Now, this point
is quite arguable; a pure association table doesn't seem to benefit much from a surrogate key (though a
collection of composite values might). Nevertheless, Hibernate provides a feature that allows you to map
many to many associations and collections of values to a table with a surrogate key.
T he <idbag> element lets you map a List (or Collection) with bag semantics.
112
第6章 コレクションのマッピング
<idbag name="lovers" table="LOVERS">
<collection-id column="ID" type="long">
<generator class="sequence"/>
</collection-id>
<key column="PERSON1"/>
<many-to-many column="PERSON2" class="Person" fetch="join"/>
</idbag>
As you can see, an <idbag> has a synthetic id generator, just like an entity class! A different surrogate
key is assigned to each collection row. Hibernate does not provide any mechanism to discover the
surrogate key value of a particular row, however.
Note that the update performance of an <idbag> is much better than a regular <bag>! Hibernate can
locate individual rows efficiently and update or delete them individually, just like a list, map or set.
In the current implementation, the native identifier generation strategy is not supported for <idbag>
collection identifiers.
6.4. コ レ ク シ ョ ン の 例
これまでのセクションの説明では理解しにくいので、以下の例を見てください。
package eg;
import java.util.Set;
public class Parent {
private long id;
private Set children;
public long getId() { return id; }
private void setId(long id) { this.id=id; }
private Set getChildren() { return children; }
private void setChildren(Set children) { this.children=children; }
....
....
}
このクラスは Child インスタンスのコレクションを持っています。もし各々の child が最大でも一つの
parent を持っているならば、最も自然なマッピングは一対多関連です。
113
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
これは以下のテーブル定義にマッピングします。
create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255), parent_id
bigint )
alter table child add constraint childfk0 (parent_id) references parent
もし parent が 要求 されるなら、双方向の一対多関連を使用してください:
<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
<many-to-one name="parent" class="Parent" column="parent_id" notnull="true"/>
</class>
</hibernate-mapping>
NOT NULL 制約に注意してください。
114
第6章 コレクションのマッピング
create table parent ( id bigint not null primary key )
create table child ( id bigint not null
primary key,
name varchar(255),
parent_id bigint not null )
alter table child add constraint childfk0 (parent_id) references parent
Alternatively, if you absolutely insist that this association should be unidirectional, you can declare the
NOT NULL constraint on the <key> mapping:
<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
一方で、もし child が複数の parent を持てるならば、多対多関連が妥当です:
<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" table="childset">
<key column="parent_id"/>
<many-to-many class="Child" column="child_id"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
テーブル定義は以下のようになります:
115
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255) )
create table childset ( parent_id bigint not null,
child_id bigint not null,
primary key ( parent_id, child_id ) )
alter table childset add constraint childsetfk0 (parent_id) references parent
alter table childset add constraint childsetfk1 (child_id) references child
For more examples and a complete walk-through a parent/child relationship mapping, see 21章例: 親/子
供.
また、さらに特殊な関連マッピングも可能です。次の章で詳しく述べます。
116
第7章 関連マッピング
第 7章 関連マッピング
7.1. イ ン ト ロ ダ ク シ ョ ン
Association mappings are the often most difficult thing to get right. In this section we'll go through the
canonical cases one by one, starting with unidirectional mappings, and then considering the bidirectional
cases. We'll use Person and Address in all the examples.
We'll classify associations by whether or not they map to an intervening join table, and by multiplicity.
null 可能な外部キーは従来型データモデリングの中では良い習慣と見なされていないため、すべての例で
not null の外部キーを使用します。これは Hibernate の要件ではありません。 not null 制約を外したとして
も、マッピングは問題なく動作します。
7.2. 単 方 向 関 連
7.2.1. 多対一
単方向多対一関連 は単方向関連の中で最も一般的なものです。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table Person ( personId bigint not null primary key, addressId bigint not
null )
create table Address ( addressId bigint not null primary key )
7.2.2. 一対一
外部キーの単方向一対一関連 はほとんど同じものです。唯一違うのは、カラムのユニークな制約です。
117
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table Person ( personId bigint not null primary key, addressId bigint not
null unique )
create table Address ( addressId bigint not null primary key )
A unidirectional one-to-one association on a primary key usually uses a special id generator. (Notice that
we've reversed the direction of the association in this example.)
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person" constrained="true"/>
</class>
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
7.2.3. 一対多
外部キーの単方向一対多関連 はとても特殊なケースで、あまり推奨されていません。
118
第7章 関連マッピング
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses">
<key column="personId"
not-null="true"/>
<one-to-many class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table Person ( personId bigint not null primary key )
create table Address ( addressId bigint not null primary key, personId bigint not
null )
We think it's better to use a join table for this kind of association.
7.3. 結 合 テ ー ブ ル を 使 っ た 単 方 向 関 連
7.3.1. 一対多
A unidirectional one-to-many association on a join table is much preferred. Notice that by specifying
unique="true", we have changed the multiplicity from many-to-many to one-to-many.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table
create table
not null
create table
Person ( personId bigint not null primary key )
PersonAddress ( personId not null, addressId bigint
primary key )
Address ( addressId bigint not null primary key )
119
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
7.3.2. 多対一
結合テーブルの単方向多対一関連 は関連が任意であるときに非常に一般的なものです。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId" unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create
create
bigint
create
table Person ( personId bigint not null primary key )
table PersonAddress ( personId bigint not null primary key, addressId
not null )
table Address ( addressId bigint not null primary key )
7.3.3. 一対一
結合テーブルの単方向一対一関連 は、非常に特殊ですが不可能ではありません。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
120
第7章 関連マッピング
create table
create table
bigint
not null
create table
Person ( personId bigint not null primary key )
PersonAddress ( personId bigint not null primary key, addressId
unique )
Address ( addressId bigint not null primary key )
7.3.4. 多対多
最後に、 単方向多対多関連 を示します。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table
create table
primary
create table
Person ( personId bigint not null primary key )
PersonAddress ( personId bigint not null, addressId bigint not null,
key (personId, addressId) )
Address ( addressId bigint not null primary key )
7.4. 双 方 向 関 連
7.4.1. 一対多 /多対一
双方向多対一関連 は最も一般的な関連です。 (標準的な親子関係です)
121
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true">
<key column="addressId"/>
<one-to-many class="Person"/>
</set>
</class>
create table Person ( personId bigint not null primary key, addressId bigint not
null )
create table Address ( addressId bigint not null primary key )
If you use a List (or other indexed collection) you need to set the key column of the foreign key to not
null, and let Hibernate manage the association from the collections side to maintain the index of each
element (making the other side virtually inverse by setting update="false" and insert="false"):
<class name="Person">
<id name="id"/>
...
<many-to-one name="address"
column="addressId"
not-null="true"
insert="false"
update="false"/>
</class>
<class name="Address">
<id name="id"/>
...
<list name="people">
<key column="addressId" not-null="true"/>
<list-index column="peopleIdx"/>
<one-to-many class="Person"/>
</list>
</class>
It is important that you define not-null="true" on the <key> element of the collection mapping if the
underlying foreign key column is NOT NULL. Don't only declare not-null="true" on a possible
nested <colum n> element, but on the <key> element.
7.4.2. 一対一
外部キーの双方向一対一関連 は非常に一般的です。
122
第7章 関連マッピング
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<one-to-one name="person"
property-ref="address"/>
</class>
create table Person ( personId bigint not null primary key, addressId bigint not
null unique )
create table Address ( addressId bigint not null primary key )
主キーの双方向一対一関連 は特殊な ID ジェネレータを使います。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<one-to-one name="address"/>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person"
constrained="true"/>
</class>
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
7.5. 結 合 テ ー ブ ル を 使 っ た 双 方 向 関 連
7.5.1. 一対多 /多対一
A bidirectional one-to-many association on a join table. Note that the inverse="true" can go on either
end of the association, on the collection, or on the join.
123
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses"
table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
inverse="true"
optional="true">
<key column="addressId"/>
<many-to-one name="person"
column="personId"
not-null="true"/>
</join>
</class>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null
primary key )
create table Address ( addressId bigint not null primary key )
7.5.2. 一対一
結合テーブルの双方向一対一関連 は非常に特殊ですが、可能です。
124
第7章 関連マッピング
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true"
inverse="true">
<key column="addressId"
unique="true"/>
<many-to-one name="person"
column="personId"
not-null="true"
unique="true"/>
</join>
</class>
create table
create table
bigint
not null
create table
Person ( personId bigint not null primary key )
PersonAddress ( personId bigint not null primary key, addressId
unique )
Address ( addressId bigint not null primary key )
7.5.3. 多対多
最後に、 双方向多対多関連 を示します。
125
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true" table="PersonAddress">
<key column="addressId"/>
<many-to-many column="personId"
class="Person"/>
</set>
</class>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null,
primary key
(personId, addressId) )
create table Address ( addressId bigint not null primary key )
7.6. よ り 複 雑 な 関 連 マ ッ ピ ン グ
より複雑な関連結合は 極めて 稀です。マッピングドキュメントに SQL 文を埋め込むことで、さらに複雑
な状況を扱うことができます。例えば、 accountNum ber 、 effectiveEndDate 、
effectiveStartDate カラムを持つ account (口座)情報の履歴を扱うテーブルは、以下のように
マッピングします。
<properties name="currentAccountKey">
<property name="accountNumber" type="string" not-null="true"/>
<property name="currentAccount" type="boolean">
<formula>case when effectiveEndDate is null then 1 else 0 end</formula>
</property>
</properties>
<property name="effectiveEndDate" type="date"/>
<property name="effectiveStateDate" type="date" not-null="true"/>
そして、関連を 現時点の インスタンス (effectiveEndDate が null であるもの)にマッピングしま
す。以下のようになります:
<many-to-one name="currentAccountInfo"
property-ref="currentAccountKey"
class="AccountInfo">
<column name="accountNumber"/>
<formula>'1'</formula>
</many-to-one>
In a more complex example, imagine that the association between Em ployee and Organization is
126
第7章 関連マッピング
maintained in an Em ploym ent table full of historical employment data. T hen an association to the
employee's most recent employer (the one with the most recent startDate) might be mapped this way:
<join>
<key column="employeeId"/>
<subselect>
select employeeId, orgId
from Employments
group by orgId
having startDate = max(startDate)
</subselect>
<many-to-one name="mostRecentEmployer"
class="Organization"
column="orgId"/>
</join>
この機能は非常に強力です。しかしこのような場合、普通は HQL や criteria クエリを使う方がより実践的
です。
127
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 8章 コンポーネントのマッピング
コンポーネント の概念は、 Hibernate を通して様々な状況の中で異なる目的のために再利用されます。
8.1. 依 存 オ ブ ジ ェ ク ト
A component is a contained object that is persisted as a value type, not an entity reference. T he term
"component" refers to the object-oriented notion of composition (not to architecture-level components).
For example, you might model a person like this:
public class Person {
private java.util.Date birthday;
private Name name;
private String key;
public String getKey() {
return key;
}
private void setKey(String key) {
this.key=key;
}
public java.util.Date getBirthday() {
return birthday;
}
public void setBirthday(java.util.Date birthday) {
this.birthday = birthday;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
......
......
}
128
第8章 コンポーネントのマッピング
public class Name {
char initial;
String first;
String last;
public String getFirst() {
return first;
}
void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
void setLast(String last) {
this.last = last;
}
public char getInitial() {
return initial;
}
void setInitial(char initial) {
this.initial = initial;
}
}
Now Nam e may be persisted as a component of Person. Notice that Nam e defines getter and setter
methods for its persistent properties, but doesn't need to declare any interfaces or identifier properties.
マッピング定義は以下のようになります。
<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name"> <!-- class attribute optional -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class>
Person テーブルは pid、 birthday、 initial、 first、 last カラムを持ちます。
Like all value types, components do not support shared references. In other words, two persons could
have the same name, but the two person objects would contain two independent name ojects, only "the
same" by value. T he null value semantics of a component are ad hoc. When reloading the containing
object, Hibernate will assume that if all component columns are null, then the entire component is null.
T his should be okay for most purposes.
コンポーネントの属性はどんな Hibernate の型でも構いません(コレクション、 many-to-one 関連、他の
コンポーネントなど)。ネストされたコンポーネントは滅多に使わないと考えるべきでは ありません 。
Hibernate は非常にきめの細かいオブジェクトモデルをサポートするように意図されています。
T he <com ponent> element allows a <parent> subelement that maps a property of the component
class as a reference back to the containing entity.
129
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name" unique="true">
<parent name="namedPerson"/> <!-- reference back to the Person -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class>
8.2. 従 属 す る オ ブ ジ ェ ク ト の コ レ ク シ ョ ン
Collections of components are supported (eg. an array of type Nam e). Declare your component
collection by replacing the <elem ent> tag with a <com posite-elem ent> tag.
<set name="someNames" table="some_names" lazy="true">
<key column="id"/>
<composite-element class="eg.Name"> <!-- class attribute required -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</composite-element>
</set>
注記: コンポジットエレメントの Set を定義したなら、 equals() と hashCode() を正しく実装するこ
とが重要です。
Composite elements may contain components but not collections. If your composite element itself
contains components, use the <nested-com posite-elem ent> tag. T his is a pretty exotic case - a
collection of components which themselves have components. By this stage you should be asking
yourself if a one-to-many association is more appropriate. T ry remodelling the composite element as an
entity - but note that even though the Java model is the same, the relational model and persistence
semantics are still slightly different.
Please note that a composite element mapping doesn't support null-able properties if you're using a
<set>. Hibernate has to use each columns value to identify a record when deleting objects (there is no
separate primary key column in the composite element table), which is not possible with null values. You
have to either use only not-null properties in a composite-element or choose a <list>, <m ap>, <bag>
or <idbag>.
A special case of a composite element is a composite element with a nested <m any-to-one> element.
A mapping like this allows you to map extra columns of a many-to-many association table to the
composite element class. T he following is a many-to-many association from Order to Item where
purchaseDate, price and quantity are properties of the association:
130
第8章 コンポーネントのマッピング
<class name="eg.Order" .... >
....
<set name="purchasedItems" table="purchase_items" lazy="true">
<key column="order_id">
<composite-element class="eg.Purchase">
<property name="purchaseDate"/>
<property name="price"/>
<property name="quantity"/>
<many-to-one name="item" class="eg.Item"/> <!-- class attribute is
optional -->
</composite-element>
</set>
</class>
Of course, there can't be a reference to the purchae on the other side, for bidirectional association
navigation. Remember that components are value types and don't allow shared references. A single
Purchase can be in the set of an Order, but it can't be referenced by the Item at the same time.
3項関連(あるいは4項など)も可能です。
<class name="eg.Order" .... >
....
<set name="purchasedItems" table="purchase_items" lazy="true">
<key column="order_id">
<composite-element class="eg.OrderLine">
<many-to-one name="purchaseDetails class="eg.Purchase"/>
<many-to-one name="item" class="eg.Item"/>
</composite-element>
</set>
</class>
コンポジットエレメントは他のエンティティへの関連として、同じシンタックスを使っているクエリ内で
使用できます。
8.3. Map の イ ン デ ッ ク ス と し て の コ ン ポ ー ネ ン ト
T he <com posite-m ap-key> element lets you map a component class as the key of a Map. Make sure
you override hashCode() and equals() correctly on the component class.
8.4. 複 合 識 別 子 と し て の コ ン ポ ー ネ ン ト
コンポーネントをエンティティクラスの識別子として使うことができます。コンポーネントクラスは以下
の条件を満たす必要があります。
java.io.Serializable を実装しなければなりません。
It must re-implement equals() and hashCode(), consistently with the database's notion of
composite key equality.
Note: in Hibernate3, the second requirement is not an absolutely hard requirement of Hibernate. But do it
anyway.
You can't use an IdentifierGenerator to generate composite keys. Instead the application must
assign its own identifiers.
Use the <com posite-id> tag (with nested <key-property> elements) in place of the usual <id>
131
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
declaration. For example, the OrderLine class has a primary key that depends upon the (composite)
primary key of Order.
<class name="OrderLine">
<composite-id name="id" class="OrderLineId">
<key-property name="lineId"/>
<key-property name="orderId"/>
<key-property name="customerId"/>
</composite-id>
<property name="name"/>
<many-to-one name="order" class="Order"
insert="false" update="false">
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>
....
</class>
このとき、 OrderLine テーブルへ関連する外部キーもまた複合です。他のクラスのマッピングでこれを
宣言しなければなりません。 OrderLine への関連は次のようにマッピングされます。
<many-to-one name="orderLine" class="OrderLine">
<!-- the "class" attribute is optional, as usual -->
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>
(Note that the <colum n> tag is an alternative to the colum n attribute everywhere.)
OrderLine への m any-to-m any 関連も複合外部キーを使います。
<set name="undeliveredOrderLines">
<key column name="warehouseId"/>
<many-to-many class="OrderLine">
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-many>
</set>
Order にある OrderLine のコレクションは次のものを使用します。
<set name="orderLines" inverse="true">
<key>
<column name="orderId"/>
<column name="customerId"/>
</key>
<one-to-many class="OrderLine"/>
</set>
(T he <one-to-m any> element, as usual, declares no columns.)
132
第8章 コンポーネントのマッピング
OrderLine 自身がコレクションを持っている場合、同時に複合外部キーも持っています。
<class name="OrderLine">
....
....
<list name="deliveryAttempts">
<key>
<!-- a collection inherits the composite key type -->
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</key>
<list-index column="attemptId" base="1"/>
<composite-element class="DeliveryAttempt">
...
</composite-element>
</set>
</class>
8.5. 動 的 コ ン ポ ー ネ ン ト
Map 型のプロパティのマッピングも可能です。
<dynamic-component name="userAttributes">
<property name="foo" column="FOO" type="string"/>
<property name="bar" column="BAR" type="integer"/>
<many-to-one name="baz" class="Baz" column="BAZ_ID"/>
</dynamic-component>
T he semantics of a <dynam ic-com ponent> mapping are identical to <com ponent>. T he advantage
of this kind of mapping is the ability to determine the actual properties of the bean at deployment time,
just by editing the mapping document. Runtime manipulation of the mapping document is also possible,
using a DOM parser. Even better, you can access (and change) Hibernate's configuration-time
metamodel via the Configuration object.
133
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 9章 継承マッピング
9.1. 3つ の 戦 略
Hibernate は3つの基本的な継承のマッピング戦略をサポートします。
クラス階層ごとのテーブル (table-per-class-hierarchy)
table per subclass
具象クラスごとのテーブル (table-per-concrete-class)
加えて4つ目に、 Hibernate はわずかに異なる性質を持ったポリモーフィズムをサポートします。
暗黙的ポリモーフィズム
It is possible to use different mapping strategies for different branches of the same inheritance
hierarchy, and then make use of implicit polymorphism to achieve polymorphism across the whole
hierarchy. However, Hibernate does not support mixing <subclass>, and <joined-subclass> and
<union-subclass> mappings under the same root <class> element. It is possible to mix together
the table per hierarchy and table per subclass strategies, under the the same <class> element, by
combining the <subclass> and <join> elements (see below).
subclass、 union-subclass と joined-subclass マッピングを複数のマッピングドキュメントに
定義することが出来、 hibernate-m apping の直下に配置します。これは新しいマッピングファイルを
追加するだけで、クラス階層を拡張できるということです。あらかじめマップしたスーパークラスを指定
して、サブクラスマッピングに extends 属性を記述しなければなりません。注記:この特徴により、以
前はマッピングドキュメントの順番が重要でした。 Hibernate3 からは、 extends キーワードを使う場
合、マッピングドキュメントの順番は問題になりません。1つのマッピングファイル内で順番付けを行う
ときは、依然として、サブクラスを定義する前にスーパークラスを定義する必要があります。)
<hibernate-mapping>
<subclass name="DomesticCat" extends="Cat" discriminator-value="D">
<property name="name" type="string"/>
</subclass>
</hibernate-mapping>
9.1.1. クラス階層ごとのテーブル( table-per-class-hierarchy)
例えば、インターフェース Paym ent と、それを実装した CreditCardPaym ent、 CashPaym ent、
ChequePaym ent があるとします。階層ごとのテーブルマッピングは以下のようになります:
134
第9章 継承マッピング
<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<property name="creditCardType" column="CCTYPE"/>
...
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
...
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
...
</subclass>
</class>
ちょうど一つのテーブルが必要です。このマッピング戦略には一つ大きな制限があります。 CCT YPE のよ
うな、サブクラスで宣言されたカラムは NOT NULL 制約を持てません。
9.1.2. サブクラスごとのテーブル ( table-per-subclass)
table-per-subclass マッピングは以下のようになります:
<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="creditCardType" column="CCTYPE"/>
...
</joined-subclass>
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
</class>
このマッピングには4つのテーブルが必要です。3つのサブクラステーブルはスーパークラステーブルとの
関連を示す主キーを持っています (実際、関係モデル上は一対一関連です)。
9.1.3. discriminator を用いた table-per-subclass
Note that Hibernate's implementation of table per subclass requires no discriminator column. Other
object/relational mappers use a different implementation of table per subclass which requires a type
discriminator column in the superclass table. T he approach taken by Hibernate is much more difficult to
implement but arguably more correct from a relational point of view. If you would like to use a
discriminator column with the table per subclass strategy, you may combine the use of <subclass> and
<join>, as follow:
135
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<join table="CREDIT_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="creditCardType" column="CCTYPE"/>
...
</join>
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
<join table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
...
</join>
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
<join table="CHEQUE_PAYMENT" fetch="select">
<key column="PAYMENT_ID"/>
...
</join>
</subclass>
</class>
T he optional fetch="select" declaration tells Hibernate not to fetch the ChequePaym ent subclass
data using an outer join when querying the superclass.
9.1.4. table-per-subclass と table-per-class-hierarchy の混合
このアプローチを使用すると、 table-per-hierarchy と table-per-subclass 戦略を組み合わせる事も可能で
す。
<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<join table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</join>
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
...
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
...
</subclass>
</class>
For any of these mapping strategies, a polymorphic association to the root Paym ent class is mapped
136
第9章 継承マッピング
using <m any-to-one>.
<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>
9.1.5. 具象クラスごとのテーブル( table-per-concrete-class)
T here are two ways we could go about mapping the table per concrete class strategy. T he first is to use
<union-subclass>.
<class name="Payment">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="sequence"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</union-subclass>
<union-subclass name="CashPayment" table="CASH_PAYMENT">
...
</union-subclass>
<union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
...
</union-subclass>
</class>
サブクラスごとに3つのテーブルが必要です。それぞれのテーブルは、継承プロパティを含んだ、クラス
の全てのプロパティに対するカラムを定義します。
このアプローチには制限があります。それは、プロパティがスーパークラスにマッピングされていた場
合、全てのサブクラスにおいてカラム名が同じでなければならないというものです。(Hibernate の今後
のリリースで緩和されるかもしれません)。 union subclass 継承では識別子生成戦略を使用できませ
ん。主キーを生成するためのシードは、全ての union subclass の階層内で共有する必要があるからで
す。
If your superclass is abstract, map it with abstract="true". Of course, if it is not abstract, an
additional table (defaults to PAYMENT in the example above) is needed to hold instances of the
superclass.
9.1.6. 暗黙的ポリモーフィズムを用いた table-per-concrete-class
もう一つのアプローチは暗黙的ポリモーフィズムの使用です:
137
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="CreditCardPayment" table="CREDIT_PAYMENT">
<id name="id" type="long" column="CREDIT_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CREDIT_AMOUNT"/>
...
</class>
<class name="CashPayment" table="CASH_PAYMENT">
<id name="id" type="long" column="CASH_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CASH_AMOUNT"/>
...
</class>
<class name="ChequePayment" table="CHEQUE_PAYMENT">
<id name="id" type="long" column="CHEQUE_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CHEQUE_AMOUNT"/>
...
</class>
Notice that nowhere do we mention the Paym ent interface explicitly. Also notice that properties of
Paym ent are mapped in each of the subclasses. If you want to avoid duplication, consider using XML
entities (e.g. [ <!ENT IT Y allproperties SYST EM "allproperties.xm l"> ] in the DOCT YPE
declartion and & allproperties; in the mapping).
このアプローチの欠点は、 Hibernate がポリモーフィックなクエリの実行時に SQL UNION を生成しない
点です。
For this mapping strategy, a polymorphic association to Paym ent is usually mapped using <any>.
<any name="payment" meta-type="string" id-type="long">
<meta-value value="CREDIT" class="CreditCardPayment"/>
<meta-value value="CASH" class="CashPayment"/>
<meta-value value="CHEQUE" class="ChequePayment"/>
<column name="PAYMENT_CLASS"/>
<column name="PAYMENT_ID"/>
</any>
9.1.7. 他の継承マッピングと暗黙的ポリモーフィズムの組み合わせ
T here is one further thing to notice about this mapping. Since the subclasses are each mapped in their
own <class> element (and since Paym ent is just an interface), each of the subclasses could easily be
part of another inheritance hierarchy! (And you can still use polymorphic queries against the Paym ent
interface.)
138
第9章 継承マッピング
<class name="CreditCardPayment" table="CREDIT_PAYMENT">
<id name="id" type="long" column="CREDIT_PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="CREDIT_CARD" type="string"/>
<property name="amount" column="CREDIT_AMOUNT"/>
...
<subclass name="MasterCardPayment" discriminator-value="MDC"/>
<subclass name="VisaPayment" discriminator-value="VISA"/>
</class>
<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
<id name="id" type="long" column="TXN_ID">
<generator class="native"/>
</id>
...
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="amount" column="CASH_AMOUNT"/>
...
</joined-subclass>
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="amount" column="CHEQUE_AMOUNT"/>
...
</joined-subclass>
</class>
Once again, we don't mention Paym ent explicitly. If we execute a query against the Paym ent interface for example, from Paym ent - Hibernate automatically returns instances of CreditCardPaym ent
(and its subclasses, since they also implement Paym ent), CashPaym ent and ChequePaym ent but
not instances of NonelectronicT ransaction.
9.2. 制 限
T here are certain limitations to the "implicit polymorphism" approach to the table per concrete-class
mapping strategy. T here are somewhat less restrictive limitations to <union-subclass> mappings.
次のテーブルに、 Hibernate における table-per-concrete-class マッピングの制限や暗黙的ポリモーフィ
ズムの制限を示します。
139
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
表 9.1 継承マッピングの機能
継承戦
略
多対一の
ポリモー
フィズム
一対一の
ポリモー
フィズム
一対多の
ポリモー
フィズム
多対多の
ポリモー
フィズム
ポリモー
フィック
な
load()/
get()
ポリ
モー
フィズ
ムを
使った
クエリ
ポリ
モー
フィズ
ムを
使った
結合
外部結
合によ
る
フェッ
チ
table per
classhierarchy
<m anytoone>
<onetoone>
<onetom any>
<m anytom any>
s.get(P
aym ent
.class,
id)
from
Paym en
t p
from
Order
o join
o.paym
ent p
supporte
d
table per
subclass
<m anytoone>
<onetoone>
<onetom any>
<m anytom any>
s.get(P
aym ent
.class,
id)
from
Paym en
t p
from
Order
o join
o.paym
ent p
supporte
d
table per
concrete
-class
(unionsubclass
)
<m anytoone>
<onetoone>
<onetom any>
(for
invers
e="tru
e" only)
<m anytom any>
s.get(P
aym ent
.class,
id)
from
Paym en
t p
from
Order
o join
o.paym
ent p
supporte
d
table per
concrete
class
(implicit
polymorp
hism)
<any>
not
supporte
d
not
supporte
d
<m anytoany>
s.crea
teCrit
eria(P
aym ent
.class)
.add(
Restri
ctions
.idEq(
id)
).uniqu
eResul
t()
from
Paym en
t p
not
supporte
d
not
supporte
d
14 0
第10章 オブジェクトを扱う
第 10章 オブジェクトを扱う
Hibernate は完全なオブジェクト/リレーショナルマッピングソリューションであり、データベース管理シ
ステムの詳細を開発者から隠蔽するだけでなく、オブジェクトの 状態管理 も行います。これは、
JDBC/SQL 永続層と同じような SQL statem ents の管理とは異なり、 Java アプリケーションにおける
永続化に対する、とても自然なオブジェクト指向の考え方を提供します。
言いかえれば、 Hibernate を用いるアプリケーション開発者は、オブジェクトの 状態 については常に意
識すべきであり、 SQL 文の実行については必ずしもそうではありません。この部分は、通常、 Hibernate
が処理し、システムのパフォーマンスをチューニングするときにだけ、問題になってきます。
10.1. Hibernate に お け る オ ブ ジ ェ ク ト の 状 態
Hibernate は次のようなオブジェクトの状態を定義し、サポートしています:
Transient - an object is transient if it has just been instantiated using the new operator, and it is not
associated with a Hibernate Session. It has no persistent representation in the database and no
identifier value has been assigned. T ransient instances will be destroyed by the garbage collector if
the application doesn't hold a reference anymore. Use the Hibernate Session to make an object
persistent (and let Hibernate take care of the SQL statements that need to be executed for this
transition).
Persistent - a persistent instance has a representation in the database and an identifier value. It
might just have been saved or loaded, however, it is by definition in the scope of a Session.
Hibernate will detect any changes made to an object in persistent state and synchronize the state
with the database when the unit of work completes. Developers don't execute manual UPDAT E
statements, or DELET E statements when an object should be made transient.
Detached - detached インスタンスとは、永続化されているが、それと関連付いていた Session がク
ローズされているオブジェクトのことです。そのオブジェクトへの参照は、依然として有効です。そ
して、もちろん、detached インスタンスはこの状態に修正することさえできます。 detacched インス
タンスは、もう一度永続化したい(そして、すべての変更を永続化したい)ときに、新しい Session
に再追加できます。この機能は、ユーザーが考える時間を必要とするような、長期間に及ぶ作業単位
に対するプログラミングモデルを可能にします。我々は、これを アプリケーションのトランザクショ
ン(application transactions) と呼んでいます。すなわち、ユーザーから見た作業単位だということ
です。
We'll now discuss the states and state transitions (and the Hibernate methods that trigger a transition)
in more detail.
10.2. オ ブ ジ ェ ク ト を 永 続 状 態 に す る
新しくインスタンス化された永続クラスのインスタンスは、 Hibernate では transient と見なされます。
以下のように、セッションと関連づけることで、 transient インスタンスを 永続状態 (persistent) にでき
ます。
DomesticCat fritz = new DomesticCat();
fritz.setColor(Color.GINGER);
fritz.setSex('M');
fritz.setName("Fritz");
Long generatedId = (Long) sess.save(fritz);
Cat クラスの識別子が自動生成されるのであれば、 save() が呼ばれるときに、識別子が生成され、 cat
インスタンスに割り当てられます。 Cat の識別子が assigned 識別子を持つか、複合キーであるなら、
save() を呼び出す前に、識別子を cat インスタンスを割り当てなければなりません。 save() の代わり
14 1
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
に、 EJB3 の初期ドラフトで定義された persist() を使うことも可能です。
代わりに、識別子を引数にとる save() メソッドを使って、識別子を割り当てることもできます。
DomesticCat pk = new DomesticCat();
pk.setColor(Color.TABBY);
pk.setSex('F');
pk.setName("PK");
pk.setKittens( new HashSet() );
pk.addKitten(fritz);
sess.save( pk, new Long(1234) );
永続化するオブジェクトが関連オブジェクトを持っている場合 (例えば、前の例における kittens コレ
クションのように)、外部キーカラムに、 NOT NULL 制約をつけない限りは、これらの一連のオブジェ
クトをどんな順番で永続化してもかまいません。外部キー制約を違反する恐れはありません。しかし、
NOT NULL 制約がある場合、間違った順番でオブジェクトを save() してしまうと、制約に違反するかも
しれません。
Usually you don't bother with this detail, as you'll very likely use Hibernate's transitive persistence
feature to save the associated objects automatically. T hen, even NOT NULL constraint violations don't
occur - Hibernate will take care of everything. T ransitive persistence is discussed later in this chapter.
10.3. オ ブ ジ ェ ク ト の ロ ー ド
永続化されたインスタンスの識別子があらかじめ分かっているなら、 Session の load() メソッドを
使って、復元できます。 load() は、 Class オブジェクトを引数にとり、そのクラスのインスタンスを
新たに生成し、状態をロードします。そのインスタンスの状態は、永続 (persistent) 状態です。
Cat fritz = (Cat) sess.load(Cat.class, generatedId);
// you need to wrap primitive identifiers
long id = 1234;
DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );
あるいは、以下のように、既存のインスタンスに状態をロードすることもできます:
Cat cat = new DomesticCat();
// load pk's state into cat
sess.load( cat, new Long(pkId) );
Set kittens = cat.getKittens();
DB に該当する行が無い場合、 load() は回復不可能な例外を投げることに注意しましょう。そのクラス
がプロキシを使ってマッピングされている場合、 load() は初期化されていないプロキシを返し、プロキ
シのメソッドが呼ばれるまで実際にはデータベースにアクセスしません。もし、実際にデータベースから
ロードせずに、オブジェクトに対する関連を作りたい場合、この振る舞いはとても役立ちます。 batchsize がクラスマッピングに定義されているならば、複数のインスタンスを一括でロードすることが可能
です。
該当する行が存在することを確信できない場合は、 get() メソッドを使うべきです。それは、データ
ベースにすぐにアクセスし、該当する行が無い場合は null を返します。
14 2
第10章 オブジェクトを扱う
Cat cat = (Cat) sess.get(Cat.class, id);
if (cat==null) {
cat = new Cat();
sess.save(cat, id);
}
return cat;
LockMode を使えば、 SELECT ... FOR UPDAT E という SQL を使ってオブジェクトをロードすること
ができます。詳細な情報は、 API ドキュメントを参照してください。
Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);
関連に対するカスケード方法として lock や all を指定しない限り、関連するインスタンスや含まれる
コレクションは FOR UPDAT E で復元 されない ことに注意しましょう。
refresh() メソッドを使うことで、どんなときでも、オブジェクトやそのコレクションをリロードする
ことができます。データベースのトリガがテーブルを更新した際に、そのテーブルに対応するオブジェク
トのプロパティを同期する場合、このメソッドが役に立ちます。
sess.save(cat);
sess.flush(); //force the SQL INSERT
sess.refresh(cat); //re-read the state (after the trigger executes)
An important question usually appears at this point: How much does Hibernate load from the database
and how many SQL SELECT s will it use? T his depends on the fetching strategy and is explained in
「フェッチ戦略」.
10.4. ク エ リ
If you don't know the identifiers of the objects you are looking for, you need a query. Hibernate supports
an easy-to-use but powerful object oriented query language (HQL). For programmatic query creation,
Hibernate supports a sophisticated Criteria and Example query feature (QBC and QBE). You may also
express your query in the native SQL of your database, with optional support from Hibernate for result
set conversion into objects.
10.4.1. クエリの実行
HQL やネイティブな SQL クエリは、 org.hibernate.Query のインスタンスとして表現されます。こ
のインタフェースは、パラメータバインディングや ResultSet のハンドリングやクエリの実行を行うメ
ソッドを用意しています。通常、 Query は、以下に示すように、その時点の Session を使って取得し
ます。
14 3
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
List cats = session.createQuery(
"from Cat as cat where cat.birthdate < ?")
.setDate(0, date)
.list();
List mothers = session.createQuery(
"select mother from Cat as cat join cat.mother as mother where cat.name = ?")
.setString(0, name)
.list();
List kittens = session.createQuery(
"from Cat as cat where cat.mother = ?")
.setEntity(0, pk)
.list();
Cat mother = (Cat) session.createQuery(
"select cat.mother from Cat as cat where cat = ?")
.setEntity(0, izi)
.uniqueResult();]]
Query mothersWithKittens = (Cat) session.createQuery(
"select mother from Cat as mother left join fetch mother.kittens");
Set uniqueMothers = new HashSet(mothersWithKittens.list());
クエリは、普通、 list() を呼び出すことによって実行されます。クエリの結果は、メモリ上にあるコレ
クションにすべてロードされます。クエリによって復元されたエンティティのインスタンスは、永続状態
です。もし、クエリがたった1個のインスタンスを返すと分かっているなら、 uniqueResult() メソッ
ドが手っ取り早い方法です。即時フェッチを利用したクエリの場合、ふつう、得られたコレクションに
は、ルートのオブジェクトが重複して含まれています(しかし、ルートが持つコレクションは初期化
(ロード)されています)。この重複は Set を使って取り除くことができます。
10.4 .1.1. 結果をイテレートする
時々、 iterate() メソッドを使ってクエリを実行することで、より良いパフォーマンスを得ることがで
きます。これは、通常、クエリによって得られた実際のエンティティのインスタンスが、すでにセッショ
ンまたは二次キャッシュに存在することが期待できる場合だけです。それらが、まだキャッシュされてい
ないなら、 iterate() は、 list() よりも遅く、簡単なクエリに対しても多くのデータベースアクセス
を必要とします。そのアクセスとは、識別子だけを取得するための最初の select 1回 と、実際のインス
タンスを初期化するために後から行う n回 の select のことです。
// fetch ids
Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
while ( iter.hasNext() ) {
Qux qux = (Qux) iter.next(); // fetch the object
// something we couldnt express in the query
if ( qux.calculateComplicatedAlgorithm() ) {
// delete the current instance
iter.remove();
// dont need to process the rest
break;
}
}
10.4 .1.2. オブジェクトの組( tuple)を返すクエリ
Hibernate のクエリでは、時々、オブジェクトの組を返すことがあります。その場合は、各タプルは配列
として返されます:
14 4
第10章 オブジェクトを扱う
Iterator kittensAndMothers = sess.createQuery(
"select kitten, mother from Cat kitten join kitten.mother mother")
.list()
.iterator();
while ( kittensAndMothers.hasNext() ) {
Object[] tuple = (Object[]) kittensAndMothers.next();
Cat kitten = tuple[0];
Cat mother = tuple[1];
....
}
10.4 .1.3. スカラーの結果
Queries may specify a property of a class in the select clause. T hey may even call SQL aggregate
functions. Properties or aggregates are considered "scalar" results (and not entities in persistent state).
Iterator results = sess.createQuery(
"select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
"group by cat.color")
.list()
.iterator();
while ( results.hasNext() ) {
Object[] row = (Object[]) results.next();
Color type = (Color) row[0];
Date oldest = (Date) row[1];
Integer count = (Integer) row[2];
.....
}
10.4 .1.4 . パラメータのバインド
Query は、名前付きのパラメータや JDBC スタイルの ? パラメータに値をバインドするためのメソッド
を持っています。 JDBC とは違い、 Hibernate はパラメータにゼロから番号を振っていきます。名前付き
のパラメータとは、クエリ文字列のなかにある :nam e 形式の識別子です。名前付きパラメータの利点は
次の通りです。
名前付きパラメータは、クエリ文字列に登場する順番と無関係です
同じクエリ内に複数回登場することができます
自分自身を説明します
//named parameter (preferred)
Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
q.setString("name", "Fritz");
Iterator cats = q.iterate();
//positional parameter
Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
q.setString(0, "Izi");
Iterator cats = q.iterate();
14 5
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
//named parameter list
List names = new ArrayList();
names.add("Izi");
names.add("Fritz");
Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
q.setParameterList("namesList", names);
List cats = q.list();
10.4 .1.5. ページ分け
ResultSet に制限(復元したい最大行数や復元したい最初の行)を加える必要があれば、以下のように、
Query インターフェースのメソッドを使います。
Query q = sess.createQuery("from DomesticCat cat");
q.setFirstResult(20);
q.setMaxResults(10);
List cats = q.list();
制限付きのクエリを DBMS のネイティブな SQL に変換する方法を、 Hibernate は知っています。
10.4 .1.6. スクロール可能なイテレーション
JDBC ドライバがスクロール可能な ResultSet をサポートしていれば、 Query インターフェースを
使って、 ScrollableResults オブジェクトを取得できます。それを使うと、クエリの結果に対して柔
軟にナビゲーションできます。
Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
"order by cat.name");
ScrollableResults cats = q.scroll();
if ( cats.first() ) {
// find the first name on each page of an alphabetical list of cats by name
firstNamesOfPages = new ArrayList();
do {
String name = cats.getString(0);
firstNamesOfPages.add(name);
}
while ( cats.scroll(PAGE_SIZE) );
// Now get the first page of cats
pageOfCats = new ArrayList();
cats.beforeFirst();
int i=0;
while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );
}
cats.close()
この機能にはオープン状態のデータベースコネクションが必要であることに注意してください。もし、オ
フラインのページ分け機能が必要であれば、 setMaxResult() / setFirstResult() を使いましょ
う。
10.4 .1.7. 名前付きクエリの外出し
マッピングドキュメントに名前付きのクエリを定義することができます。(マークアップと解釈される文
字がクエリに含まれるなら、 CDAT A セクションを使うことを忘れないようにしましょう。)
14 6
第10章 オブジェクトを扱う
<query name="ByNameAndMaximumWeight"><![CDATA[
from eg.DomesticCat as cat
where cat.name = ?
and cat.weight > ?
] ]></query>
パラメータのバインディングと実行は、以下のようなプログラムで行われます:
Query q = sess.getNamedQuery("ByNameAndMaximumWeight");
q.setString(0, name);
q.setInt(1, minWeight);
List cats = q.list();
実際のプログラムコードは、使われるクエリ言語に依存していないことに注意しましょう。メタデータに
は、ネイティブ SQL クエリを定義することもできます。また、既存のクエリをマッピングファイルに移
すことで、 Hibernate に移行することもできます。
Also note that a query declaration inside a <hibernate-m apping> element requires a global unique
name for the query, while a query declaration inside a <class> element is made unique automatically by
prepending the fully qualified name of the class, for example eg.Cat.ByNam eAndMaxim um Weight.
10.4.2. フィルタリングコレクション
コレクション フィルタ は、永続化されているコレクションや配列に適用される特殊なタイプのクエリで
す。そのクエリ文字列では、コレクションのその時点での要素を意味する this を使います。
Collection blackKittens = session.createFilter(
pk.getKittens(),
"where this.color = ?")
.setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
.list()
);
T he returned collection is considered a bag, and it's a copy of the given collection. T he original collection
is not modified (this is contrary to the implication of the name "filter", but consistent with expected
behavior).
フィルタには from 節が不要であることに気づくでしょう(必要なら、持つことも可能ですが)。フィル
タは、コレクションの要素自体を返して構いません。
Collection blackKittenMates = session.createFilter(
pk.getKittens(),
"select this.mate where this.color = eg.Color.BLACK.intValue")
.list();
クエリを含まないフィルタも役に立ちます。例えば、非常に大きなコレクションの部分集合をロードする
ために使えます。
Collection tenKittens = session.createFilter(
mother.getKittens(), "")
.setFirstResult(0).setMaxResults(10)
.list();
10.4.3. クライテリアのクエリ
14 7
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
HQL は非常に強力ですが、クエリ文字列を作るよりも、オブジェクト指向の API を使って動的にクエリを
作る方を好む開発者もいます。こういった場合のために、 Hibernate は直感的な Criteria クエリ API
を提供しています。
Criteria crit = session.createCriteria(Cat.class);
crit.add( Expression.eq( "color", eg.Color.BLACK ) );
crit.setMaxResults(10);
List cats = crit.list();
T he Criteria and the associated Exam ple API are discussed in more detail in 15章Criteria クエリ.
10.4.4. ネイティブ SQL のクエリ
createSQLQuery() を使って、 SQL でクエリを表現することもできます。そして、 Hibernate に、
ResultSet からオブジェクトへのマッピングをまかせます。 session.connection() を呼べばどんな
ときでも、直接、 JDBC Connection を使用できることを覚えておきましょう。もし、 Hibernate API
を使うのであれば、下記のように SQL の別名を括弧でくくらなければなりません。
List cats = session.createSQLQuery(
"SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
"cat",
Cat.class
).list();
List cats = session.createSQLQuery(
"SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
"{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
"FROM CAT {cat} WHERE ROWNUM<10",
"cat",
Cat.class
).list()
SQL queries may contain named and positional parameters, just like Hibernate queries. More information
about native SQL queries in Hibernate can be found in 16章ネイティブ SQL.
10.5. 永 続 オ ブ ジ ェ ク ト の 修 正
処理中の永続インスタンス (例: Session によって、ロード、セーブ、作成、クエリされたオブジェク
ト)は、アプリケーションに操作されます。その際に変更された永続状態は、 Session が フラッシュ
されるときに、永続化されます(これは、この章の後半で述べています)。変更を永続化するために、特
殊なメソッド( update() のようなもの。これは、別の目的で使用します)を呼ぶ必要はありません。オ
ブジェクトの状態を更新する一番簡単な方法は、オブジェクトを load() し、 Session をオープンにし
ている間に、直接操作することです。
DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
cat.setName("PK");
sess.flush(); // changes to cat are automatically detected and persisted
(オブジェクトをロードするための) SQL の SELECT と(更新された状態を永続化するための) SQL の
UPDAT E が同じセッションで必要となるので、このプログラミングモデルは、効率が悪くなる場合があり
ます。そのため、 Hibernate は別の方法を用意しています。それは、 detached インスタンスを使用する
方法です。
Note that Hibernate does not offer its own API for direct execution of UPDATE or DELETE statements.
14 8
第10章 オブジェクトを扱う
Hibernate is a state management service, you don't have to think in statements to use it. JDBC is a
perfect API for executing SQL statements, you can get a JDBC Connection at any time by calling
session.connection(). Furthermore, the notion of mass operations conflicts with object/relational
mapping for online transaction processing-oriented applications. Future versions of Hibernate may
however provide special mass operation functions. See 13章バッチ処理 for some possible batch
operation tricks.
10.6. detached オ ブ ジ ェ ク ト の 修 正
Many applications need to retrieve an object in one transaction, send it to the UI layer for manipulation,
then save the changes in a new transaction. Applications that use this kind of approach in a highconcurrency environment usually use versioned data to ensure isolation for the "long" unit of work.
Hibernate は、 Session.update() や Session.m erge() メソッドを使って、 detached インスタン
スを再追加することで、このモデルに対応します。
// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catId);
Cat potentialMate = new Cat();
firstSession.save(potentialMate);
// in a higher layer of the application
cat.setMate(potentialMate);
// later, in a new session
secondSession.update(cat); // update cat
secondSession.update(mate); // update mate
識別子 catId を持つ Cat が、既に secondSession でロードされていた場合は、再追加しようとした
ときに、例外が投げられます。
同じ識別子を持つ永続インスタンスをセッションが既に保持していないことを確信できるなら update()
を使います。そして、セッションの状態を考えずに、どんな場合でも変更をマージしたい場合は、
m erge() を使います。すなわち、 detached インスタンスの再追加操作が、最初に実行されることを確
実にするために、通常は update() が新しいセッションのなかで最初に呼ばれるメソッドになります。
T he application should individually update() detached instances reachable from the given detached
instance if and only if it wants their state also updated. T his can be automated of course, using transitive
persistence, see 「連鎖的な永続化」.
lock() メソッドでもまた、新しいセッションにオブジェクトを再関連付けできます。しかし、 detached
インスタンスは無修正でなければなりません。
//just reassociate:
sess.lock(fritz, LockMode.NONE);
//do a version check, then reassociate:
sess.lock(izi, LockMode.READ);
//do a version check, using SELECT ... FOR UPDATE, then reassociate:
sess.lock(pk, LockMode.UPGRADE);
lock() は、さまざまな LockMode とともに使うことができます。詳細は、 API ドキュメントとトラン
ザクション処理の章を参照してください。再追加のときにだけ、 lock() が使われるわけではありませ
ん。
Other models for long units of work are discussed in 「楽観的同時実行制御」.
14 9
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
10.7. 自 動 的 な 状 態 検 出
Hibernate のユーザーは次の2つのケースのどちらにも使える汎用的なメソッドを要求していました。それ
は、新しい識別子を生成して transient インスタンスをセーブすることと、その時点の識別子と関連づい
ている detached インスタンスを更新/再追加することのできるメソッドです。 saveOrUpdate() はこの
ような機能を実現したメソッドです。
// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catID);
// in a higher tier of the application
Cat mate = new Cat();
cat.setMate(mate);
// later, in a new session
secondSession.saveOrUpdate(cat);
secondSession.saveOrUpdate(mate);
// update existing state (cat has a non-null id)
// save the new instance (mate has a null id)
saveOrUpdate() の使用方法と意味は、新しいユーザーにとって混乱を招くかもしれません。まず第一
に、あるセッションで使用したインスタンスを別の新しいセッションで使おうとしない限り、 update()
や saveOrUpdate() や m erge() を使う必要はありません。アプリケーション全体を通じて、これらの
メソッドを全く使わないこともあります。
通常、 update() や saveOrUpdate() は次のシナリオで使われます:
アプリケーションが最初のセッションでオブジェクトをロードします。
オブジェクトが UI 層に送られます。
オブジェクトに対して変更が加えられます。
オブジェクトがビジネスロジック層に送られます。
アプリケーションは、2番目のセッションで update() を呼ぶことで、これらの変更を永続化しま
す。
saveOrUpdate() は以下のことを行います:
オブジェクトがこのセッションで、すでに永続化されていれば、何もしません。
そのセッションに関連づいている別のオブジェクトが同じ識別子を持っているなら、例外を投げま
す。
オブジェクトの識別子が値を持たないならば、 save() します。
if the object's identifier has the value assigned to a newly instantiated object, save() it
if the object is versioned (by a <version> or <tim estam p>), and the version property value is the
same value assigned to a newly instantiated object, save() it
そうでない場合は、そのオブジェクトを update() します。
そして、 m erge() は以下のように非常に異なります:
同じ識別子を持つ永続化インスタンスがその時点でセッションと関連付いているならば、引数で受け
取ったオブジェクトの状態を永続化インスタンスにコピーします。
永続化インスタンスがその時点でセッションに関連付いていないなら、データベースからそれをロー
ドするか、あるいは、新しい永続化インスタンスを作成します。
永続化インスタンスが返されます。
引数として与えたインスタンスはセッションと関連を持ちません。それは、分離状態のままです。
150
第10章 オブジェクトを扱う
10.8. 永 続 オ ブ ジ ェ ク ト の 削 除
Session.delete() will remove an object's state from the database. Of course, your application might
still hold a reference to a deleted object. It's best to think of delete() as making a persistent instance
transient.
sess.delete(cat);
外部キー制約に違反するリスクもなく、好きな順番でオブジェクトを削除することができます。ただし、
間違った順番でオブジェクトを削除すると、外部キーカラムの NOT NULL 制約に違反する可能性があり
ます。例えば、親オブジェクトを削除したときに、子供オブジェクトを削除し忘れた場合です。
10.9. 異 な る 二 つ の デ ー タ ス ト ア 間 で の オ ブ ジ ェ ク ト の レ プ リ
ケーション
永続インスタンスのグラフを別のデータストアに永続化する場合に、識別子の値を再生成せずにすむと便
利な場合があります。
//retrieve a cat from one database
Session session1 = factory1.openSession();
Transaction tx1 = session1.beginTransaction();
Cat cat = session1.get(Cat.class, catId);
tx1.commit();
session1.close();
//reconcile with a second database
Session session2 = factory2.openSession();
Transaction tx2 = session2.beginTransaction();
session2.replicate(cat, ReplicationMode.LATEST_VERSION);
tx2.commit();
session2.close();
レプリケーション先のデータベースに行が既にある場合、 replicate() が衝突をどのように扱うかを
ReplicationMode で指定します。
ReplicationMode.IGNORE - 同じ識別子を持つ行がデータベースに存在するなら、そのオブジェク
トを無視します。
ReplicationMode.OVERWRIT E - 同じ識別子を持つ既存の行をすべて上書きします。
ReplicationMode.EXCEPT ION - 同じ識別子を持つ行がデータベースに存在するなら、例外を投げ
ます。
ReplicationMode.LAT EST _VERSION - 行に保存されているバージョン番号が、引数のオブジェク
トのバージョン番号より古いならば、その行を上書きします。
次のようなケースで、この機能を使用します。異なるデータベースインスタンスに入れられたデータの同
期、製品更新時におけるシステム設定情報の更新、非 ACID トランザクションのなかで加えられた変更の
ロールバックなどです。
10.10. セ ッ シ ョ ン の フ ラ ッ シ ュ
From time to time the Session will execute the SQL statements needed to synchronize the JDBC
connection's state with the state of objects held in memory. T his process, flush, occurs by default at the
following points
151
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
クエリを実行する前
org.hibernate.T ransaction.com m it() を実行したとき
Session.flush() を実行したとき
SQL 文は以下の順番で発行されます。
1. すべてのエンティティの挿入。これは、 Session.save() を使ってセーブしたオブジェクトの順
に実行していきます。
2. すべてのエンティティの更新
3. すべてのコレクションの削除
4. すべてのコレクションの要素に対する削除、更新、挿入
5. すべてのコレクションの挿入
6. すべてのエンティティの削除。これは、 Session.delete() を使って削除したオブジェクトの順
に実行していきます。
(1つ例外があります。 native ID 生成を使ったオブジェクトは、それらがセーブされたときに挿入され
ます。)
明示的に flush() するときを除いて、 いつ Session が JDBC をコールするのかについて絶対的な保証
はありません。ただし、それらが実行される 順番 だけは保証されます。また、 Hibernate は、
Query.list(..) が古いデータや間違ったデータ返さないことを保証しています。
It is possible to change the default behavior so that flush occurs less frequently. T he FlushMode class
defines three different modes: only flush at commit time (and only when the Hibernate T ransaction
API is used), flush automatically using the explained routine, or never flush unless flush() is called
explicitly. T he last mode is useful for long running units of work, where a Session is kept open and
disconnected for a long time (see 「拡張セッションと自動バージョニング」).
sess = sf.openSession();
Transaction tx = sess.beginTransaction();
sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state
Cat izi = (Cat) sess.load(Cat.class, id);
izi.setName(iznizi);
// might return stale data
sess.find("from Cat as cat left outer join cat.kittens kitten");
// change to izi is not flushed!
...
tx.commit(); // flush occurs
sess.close();
During flush, an exception might occur (e.g. if a DML operation violates a constraint). Since handling
exceptions involves some understanding of Hibernate's transactional behavior, we discuss it in 11章ト
ランザクションと並行性.
10.11. 連 鎖 的 な 永 続 化
個々のオブジェクトをセーブしたり、削除したり、再追加したりすることはかなり面倒です。特に、関連
するオブジェクトを扱うような場合には際立ちます。よくあるのは、親子関係を扱うケースです。以下の
例を考えてみましょう:
152
第10章 オブジェクトを扱う
If the children in a parent/child relationship would be value typed (e.g. a collection of addresses or
strings), their lifecycle would depend on the parent and no further action would be required for
convenient "cascading" of state changes. When the parent is saved, the value-typed child objects are
saved as well, when the parent is deleted, the children will be deleted, etc. T his even works for
operations such as the removal of a child from the collection; Hibernate will detect this and, since valuetyped objects can't have shared references, delete the child from the database.
ここで、親と子が値型でなくエンティティであるとして同じシナリオを考えてみましょう。(例えば、カ
テゴリーと品目の関係や親と子の猫の関係です。)エンティティは、それ自身がライフサイクルを持ち、
参照の共有をサポートします。(そのため、コレクションからエンティティを削除することは、エンティ
ティ自身の削除を意味しません。)また、エンティティは、デフォルトでは、関連する他のエンティティ
へ状態をカスケードすることはありません。 Hibernate は 到達可能性による永続化 をデフォルトでは実
行しません。
Hibernate の Session の基本操作( persist(), m erge(), saveOrUpdate(), delete(),
lock(), refresh(), evict(), replicate() が含まれます)に対して、それぞれに対応するカス
ケードスタイルがあります。それぞれのカスケードスタイルには、 create, m erge, save-update,
delete, lock, refresh, evict, replicate という名前がついています。もし、関連に沿ってカ
スケードさせたい操作があるなら、マッピングファイルにそう指定しなければなりません。例えば、以下
のようにします:
<one-to-one name="person" cascade="persist"/>
カスケードスタイルは、組み合わせることができます:
<one-to-one name="person" cascade="persist,delete,lock"/>
You may even use cascade="all" to specify that all operations should be cascaded along the
association. T he default cascade="none" specifies that no operations are to be cascaded.
特殊なカスケードスタイル delete-orphan は、一対多関連にだけ適用できます。これは、関連から削
除された子供のオブジェクトに対して、 delete() 操作が適用されることを意味します。
おすすめ:
It doesn't usually make sense to enable cascade on a <m any-to-one> or <m any-to-m any>
association. Cascade is often useful for <one-to-one> and <one-to-m any> associations.
If the child object's lifespan is bounded by the lifespan of the of the parent object make it a lifecycle
object by specifying cascade="all,delete-orphan".
Otherwise, you might not need cascade at all. But if you think that you will often be working with the
parent and children together in the same transaction, and you want to save yourself some typing,
consider using cascade="persist,m erge,save-update".
Mapping an association (either a single valued association, or a collection) with cascade="all"
marks the association as a parent/child style relationship where save/update/delete of the parent results
in save/update/delete of the child or children.
Futhermore, a mere reference to a child from a persistent parent will result in save/update of the child.
T his metaphor is incomplete, however. A child which becomes unreferenced by its parent is not
automatically deleted, except in the case of a <one-to-m any> association mapped with
cascade="delete-orphan". T he precise semantics of cascading operations for a parent/child
relationship are as follows:
親が persist() に渡されたならば、すべての子は persist() に渡されます。
153
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
m erge() に渡されたならば、すべての子は m erge() に渡されます。
親が save() 、 update() 、 saveOrUpdate() に渡されたならば、すべての子は
saveOrUpdate() に渡されます。
transient または detached の子が、永続化された親に参照されたならば、 saveOrUpdate() に渡さ
れます。
親が削除されたならば、すべての子は、 delete() に渡されます。
If a child is dereferenced by a persistent parent, nothing special happens - the application should
explicitly delete the child if necessary - unless cascade="delete-orphan", in which case the
"orphaned" child is deleted.
最後に、操作のカスケードがオブジェクトグラフに適用されるのは、 コールした時 あるいは、 flushした
時 であることに注意してください。すべての操作は、その操作が実行されたときに、到達可能な関連する
エンティティに対してカスケードが可能ならカスケードします。しかし、 save-upate と deleteorphan は、 Session が flush している間に、すべての到達可能な関連するエンティティに伝播しま
す。
10.12. メ タ デ ー タ の 使 用
Hibernate requires a very rich meta-level model of all entity and value types. From time to time, this model
is very useful to the application itself. For example, the application might use Hibernate's metadata to
implement a "smart" deep-copy algorithm that understands which objects should be copied (eg. mutable
value types) and which should not (eg. immutable value types and, possibly, associated entities).
Hibernate は ClassMetadata と CollectionMetadata インタフェースと T ype 階層を通してメタ
データを公開します。メタデータインターフェースのインスタンスは、 SessionFactory から得られま
す。
Cat fritz = ......;
ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
Object[] propertyValues = catMeta.getPropertyValues(fritz);
String[] propertyNames = catMeta.getPropertyNames();
Type[] propertyTypes = catMeta.getPropertyTypes();
// get a Map of all properties which are not collections or associations
Map namedValues = new HashMap();
for ( int i=0; i<propertyNames.length; i++ ) {
if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType()
) {
namedValues.put( propertyNames[i], propertyValues[i] );
}
}
154
第11章 トランザクションと並行性
第 11章 トランザクションと並行性
Hibernate と同時実行制御について最も重要な点は、容易に理解できることです。 Hibernate は新たな
ロックの振る舞いを追加しておらず、直接 JDBC コネクションと JT A リソースを使用します。 JDBC 、
ANSI 、およびデータベース管理システム(DBMS)のトランザクション分離の仕様を少し時間をかけて勉
強することを強く推奨します。
Hibernate はメモリ内のオブジェクトをロックしません。アプリケーションは、データベーストランザク
ションの分離レベルで定義した振る舞いを期待できます。トランザクションスコープのキャッシュでもあ
る Session のお陰で、識別子やクエリにより検索したエンティティはリピータブルリードになります
(スカラー値を返すようなレポートクエリは違います)。
バージョニングによる自動的な楽観的同時実行制御に加えて、 SELECT FOR UPDAT E 文を使用して、行
を悲観的ロックするための(マイナーな) API も提供します。楽観的同時実行制御とこの API について
は、この章の後のほうで議論します。
データベーストランザクションや長い対話(conversation、ロングトランザクション)だけでなく、
Configuration、SessionFactory、および Session という粒度で Hibernate が行う同時実行制御
の議論を始めます。
11.1. session ス コ ー プ と transaction ス コ ー プ
SessionFactory は生成することが高価で、スレッドセーフなオブジェクトです。よって、アプリケー
ションのすべてのスレッドで共有すべきです。通常、アプリケーションの起動時に、 Configuration
インスタンスから1度だけ生成します。
Session は高価ではなく、スレッドセーフなオブジェクトでもありません。よって、1つの要求や1つ
の対話、1つの作業単位(unit of work)に対して1度だけ使い、その後で捨てるべきです。 Session は
必要になるまで、 JDBC Connection(もしくは DataSource)を獲得しません。ゆえに、実際に使用
するときまでリソースを消費しません。
この状況を完了させるために、データベーストランザクションについても考えなければなりません。デー
タベース内のロックの競合を少なくするために、データベーストランザクションは可能な限り短くするべ
きです。長いデータベーストランザクションは、アプリケーションの高い並列実行性を阻害します。ゆえ
に、ユーザーが考えている間(作業単位が完了するまで)データベーストランザクションを開いたままに
するのは、たいていの場合よい設計とはいえません。
作業単位というスコープとは何でしょうか?1つの Hibernate Session は、いくつかのデータベースト
ランザクションをまたがることができるでしょうか?または、スコープと一対一の関係でしょうか?いつ
Session を開き、閉じるべきでしょうか?そして、データベーストランザクション境界をどのように分
けるのでしょうか?
11.1.1. 作業単位( Unit of work)
First, don't use the session-per-operation antipattern, that is, don't open and close a Session for every
simple database call in a single thread! Of course, the same is true for database transactions. Database
calls in an application are made using a planned sequence, they are grouped into atomic units of work.
(Note that this also means that auto-commit after every single SQL statement is useless in an
application, this mode is intended for ad-hoc SQL console work. Hibernate disables, or expects the
application server to do so, auto-commit mode immediately.) Database transactions are never optional,
all communication with a database has to occur inside a transaction, no matter if you read or write data.
As explained, auto-commit behavior for reading data should be avoided, as many small transactions are
unlikely to perform better than one clearly defined unit of work. T he latter is also much more maintainable
and extensible.
155
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
マルチユーザーのクライアント/サーバーアプリケーションの中で、最もよく使われるパターンは、
session-per-request です。このモデルの中では、クライアントから( Hibernate 永続化層が動作する)
サーバーへリクエストが送られ、新しい Hibernate Session が開かれます。そして、この作業単位の中
ですべてのデータOベース処理が実行されます。作業が完了した(そして、クライアントへのレスポンス
が準備できた)時点で、 session をフラッシュし、閉じます。クライアントの要求を処理するために、1
つのデータベーストランザクションを使用するでしょう。 Session を開き、閉じる際に、データベース
トランザクションを開始し、コミットします。二つの関係は一対一です。このモデルは多くのアプリケー
ションに完全に適合します。
T he challenge lies in the implementation. Hibernate provides built-in management of the "current
session" to simplify this pattern. All you have to do is start a transaction when a server request has to
be processed, and end the transaction before the response is send to the client. You can do this in any
way you like, common solutions are ServletFilter, AOP interceptor with a pointcut on the service
methods, or a proxy/interception container. An EJB container is a standardized way to implement crosscutting aspects such as transaction demarcation on EJB session beans, declaratively with CMT . If you
decide to use programmatic transaction demarcation, prefer the Hibernate T ransaction API shown
later in this chapter, for ease of use and code portability.
Your application code can access a "current session" to process the request by simply calling
sessionFactory.getCurrentSession() anywhere and as often as needed. You will always get a
Session scoped to the current database transaction. T his has to be configured for either resourcelocal or JT A environments, see 「コンテキスト上のセッション」.
Sometimes it is convenient to extend the scope of a Session and database transaction until the "view
has been rendered". T his is especially useful in servlet applications that utilize a separate rendering
phase after the request has been processed. Extending the database transaction until view rendering is
complete is easy to do if you implement your own interceptor. However, it is not easily doable if you rely
on EJBs with container-managed transactions, as a transaction will be completed when an EJB method
returns, before rendering of any view can start. See the Hibernate website and forum for tips and
examples around this Open Session in View pattern.
11.1.2. 長い対話
session-per-request パターンは、作業単位を設計する際に役立つ考えというだけではありません。多く
のビジネスプロセスは、ユーザーとの一連の相互作用全体を要求します。その相互作用には、データベー
スアクセスが含まれます。 Web とエンタープライズアプリケーションでは、データベーストランザク
ションがユーザーとの相互作用にまで渡ることは許されません。次の例をよく考えてみてください:
ダイアログの最初の画面が開き、個々の Session とデータベーストランザクションの中でロードさ
れたデータをユーザーに見せます。ユーザーはオブジェクトを自由に修正できます。
T he user clicks "Save" after 5 minutes and expects his modifications to be made persistent; he also
expects that he was the only person editing this information and that no conflicting modification can
occur.
この作業単位を(ユーザーの視点で)長期の 対話 (もしくは、アプリケーショントランザクション )と
呼びます。アプリケーションにこれを実装する方法はたくさんあります。
最初に思いつく実装は、ユーザーが考えている間、 Session とデータベーストランザクションを開いた
ままにしておくことです。同時に修正されず、分離と原子性が保証されるように、データベース内のロッ
クは保持したままにします。もちろん、これはアンチパターンです。なぜなら、ロックの競合が発生する
と、アプリケーションが同時ユーザー数に応じてスケールアップできなくなるからです。
Clearly, we have to use several database transactions to implement the converastion. In this case,
maintaining isolation of business processes becomes the partial responsibility of the application tier. A
single conversation usually spans several database transactions. It will be atomic if only one of these
156
第11章 トランザクションと並行性
database transactions (the last one) stores the updated data, all others simply read data (e.g. in a
wizard-style dialog spanning several request/response cycles). T his is easier to implement than it might
sound, especially if you use Hibernate's features:
自動バージョニング - Hibernate は自動的に楽観的同時実行制御ができます。ユーザーが考えている間
に同時に修正がおきた場合、自動的に検出できます。通常、対話の終了時にチェックするだけです。
分離(Detached)オブジェクト - すでに議論した session-per-request パターンを使うと決定した場
合、ロードされたすべてのインスタンスは、ユーザーが考えている間は、セッションから分離された
状態になります。オブジェクトをセッションに再追加し、修正を永続化できます。これを sessionper-request-with-detached-objects パターンと呼びます。自動バージョニングを使うことで、同時に行
われる修正を分離できます。
拡張(もしくは、長い)セッション - Hibernate の Session は、データベーストランザクションをコ
ミットした後、裏で結びついている JDBC コネクションを切断できます。そして、クライアントから
の新しい要求が発生した際に、再接続できます。このパターンは、 session-per-conversation という
名で知られており、オブジェクトをセッションへ再追加することさえ不要にします。自動バージョニ
ングを使うことで、同時に行われる修正を分離できます。通常 Session を自動的にフラッシュさせ
ず、明示的にフラッシュします。
session-per-request-with-detached-objects と session-per-conversation の2つは、利点と欠点を持って
います。これについては、この章の後のほうで、楽観的同時実行制御の文脈の中で議論します。
11.1.3. オブジェクト識別子を考える
アプリケーションは、2つの異なる Session から同じ永続状態に同時にアクセスできます。しかし、2
つの Session インスタンスが永続性クラスの1つのインスタンスを共有することはできません。ゆえ
に、識別子には2つの異なる概念があるということになります。
データベース識別子
foo.getId().equals( bar.getId() )
JVM 識別子
foo==bar
T hen for objects attached to a particularSession (i.e. in the scope of a Session) the two notions are
equivalent, and JVM identity for database identity is guaranteed by Hibernate. However, while the
application might concurrently access the "same" (persistent identity) business object in two different
sessions, the two instances will actually be "different" (JVM identity). Conflicts are resolved using
(automatic versioning) at flush/commit time, using an optimistic approach.
T his approach leaves Hibernate and the database to worry about concurrency; it also provides the best
scalability, since guaranteeing identity in single-threaded units of work only doesn't need expensive
locking or other means of synchronization. T he application never needs to synchronize on any business
object, as long as it sticks to a single thread per Session. Within a Session the application may safely
use == to compare objects.
However, an application that uses == outside of a Session, might see unexpected results. T his might
occur even in some unexpected places, for example, if you put two detached instances into the same
Set. Both might have the same database identity (i.e. they represent the same row), but JVM identity is
by definition not guaranteed for instances in detached state. T he developer has to override the
equals() and hashCode() methods in persistent classes and implement his own notion of object
equality. T here is one caveat: Never use the database identifier to implement equality, use a business
key, a combination of unique, usually immutable, attributes. T he database identifier will change if a
157
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
transient object is made persistent. If the transient instance (usually together with detached instances)
is held in a Set, changing the hashcode breaks the contract of the Set. Attributes for business keys
don't have to be as stable as database primary keys, you only have to guarantee stability as long as the
objects are in the same Set. See the Hibernate website for a more thorough discussion of this issue.
Also note that this is not a Hibernate issue, but simply how Java object identity and equality has to be
implemented.
11.1.4. 一般的な問題
session-per-user-session と session-per-application アンチパターンは使ってはいけません(もちろん、
まれに例外があります)。注記:下記の問題のいくつかは、推奨されるパターンとしても出現します。設
計を決定する前に、裏の意味を理解するようにしてください。
Session はスレッドセーフではありません。 HT T P リクエスト、セッション Bean 、 Swing ワー
カーのように、同時実行が可能なものが Session インスタンスを共有すると、競合状態を引き起こ
します。(後で議論する) HttpSession の中で Hibernate Session を保持する場合、 HttpSession
へのアクセスを同期化することを考慮すべきです。さもなければ、ユーザーが十分早くリロードをク
リックすると、同時に走る2つのスレッドの中で、同じ Session が使われます。
An exception thrown by Hibernate means you have to rollback your database transaction and close
the Session immediately (discussed later in more detail). If your Session is bound to the
application, you have to stop the application. Rolling back the database transaction doesn't put your
business objects back into the state they were at the start of the transaction. T his means the
database state and the business objects do get out of sync. Usually this is not a problem, because
exceptions are not recoverable and you have to start over after rollback anyway.
T he Session caches every object that is in persistent state (watched and checked for dirty state by
Hibernate). T his means it grows endlessly until you get an OutOfMemoryException, if you keep it
open for a long time or simply load too much data. One solution for this is to call clear() and
evict() to manage the Session cache, but you most likely should consider a Stored Procedure if
you need mass data operations. Some solutions are shown in 13章バッチ処理. Keeping a Session
open for the duration of a user session also means a high probability of stale data.
11.2. デ ー タ ベ ー ス ト ラ ン ザ ク シ ョ ン 境 界
データベース (もしくはシステム) トランザクションの境界は、常に必要です。データベーストランザ
クションの外で、データベースとの通信は起きません (これは自動コミットモードに慣れている多くの開
発者を混乱させるかもしれません) 。読み込むだけの操作にでも、いつも明確なトランザクション境界を
使用してください。分離レベルとデータベースの能力次第で、これは必要ないかもしれませんが、常にト
ランザクション境界を明示的に指定しても、マイナス面は全くありません。確かに、1つのデータベース
トランザクションは多数の小さなトランザクションより (データの読み込みであっても) パフォーマン
スがすぐれています。
J2EE 環境に管理されていない状態 (すなわち、スタンドアロン、単純な Web や Swing アプリケーショ
ン)でも、管理された状態でも、 Hibernate アプリケーションを実行できます。管理されていない環境で
は、 Hiberante がデータベースのコネクションプールを提供します。アプリケーション開発者は、トラン
ザクション境界を手動で設定しなければなりません。言い換えると、データベーストランザクションの開
始、コミット、ロールバックを開発者自身が設定する必要があるということです。通常、管理された環境
では、コンテナ管理によるトランザクション (CMT ) が提供されます。例えば、セッション Bean のデ
プロイメントディスクリプタで宣言的に定義し、トランザクションを組み立てます。プログラムによるト
ランザクション境界はもう必要ありません。
However, it is often desirable to keep your persistence layer portable between non-managed resourcelocal environments, and systems that can rely on JT A but use BMT instead of CMT . In both cases you'd
use programmatic transaction demaracation. Hibernate offers a wrapper API called T ransaction that
158
第11章 トランザクションと並行性
translates into the native transaction system of your deployment environment. T his API is actually
optional, but we strongly encourage its use unless you are in a CMT session bean.
通常、 Session 終了は、4つの異なるフェーズを含みます:
セッションのフラッシュ
トランザクションのコミット
セッションのクローズ
例外のハンドリング
Flushing the session has been discussed earlier, we'll now have a closer look at transaction
demarcation and exception handling in both managed- and non-managed environments.
11.2.1. 管理されていない環境
Hibernate 永続化層を管理されていない環境で実装する場合は、通常単純なコネクションプール (すなわ
ち DataSource ではない) によって、データベースコネクションを制御します。 Hibernate はそのコネク
ションプールから必要なコネクションを取得します。セッション/トランザクション制御のイディオムは
次のようになります:
// Non-managed environment idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}
You don't have to flush() the Session explicitly - the call to com m it() automatically triggers the
synchronization (depending upon the 「セッションのフラッシュ」 FlushMode for the session. A call to
close() marks the end of a session. T he main implication of close() is that the JDBC connection will
be relinquished by the session. T his Java code is portable and runs in both non-managed and JT A
environments.
A much more flexible solution is Hibernate's built-in "current session" context management, as
described earlier:
159
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
// Non-managed environment idiom with getCurrentSession()
try {
factory.getCurrentSession().beginTransaction();
// do some work
...
factory.getCurrentSession().getTransaction().commit();
}
catch (RuntimeException e) {
factory.getCurrentSession().getTransaction().rollback();
throw e; // or display error message
}
You will very likely never see these code snippets in a regular application; fatal (system) exceptions
should always be caught at the "top". In other words, the code that executes Hibernate calls (in the
persistence layer) and the code that handles Runtim eException (and usually can only clean up and
exit) are in different layers. T he current context management by Hibernate can significantly simplify this
design, as all you need is access to a SessionFactory. Exception handling is discussed later in this
chapter.
Note that you should select org.hibernate.transaction.JDBCT ransactionFactory (which is
the default), and for the second example "thread" as your
hibernate.current_session_context_class.
11.2.2. JTA を使用する
永続化層をアプリケーションサーバー (例えば、 EJB セッション Bean の背後) で実行する場合、
Hibernate から取得するすべてのデータソースコネクションは、自動的にグローバル JT A トランザクショ
ンの一部になります。 EJB を使わずに、スタンドアロンの JT A 実装を導入することもできます。 JT A 統
合のために、 Hibernate は2つの戦略を提供します。
Bean 管理トランザクション(BMT )を使い、 T ransaction API を使う場合、 Hibernate はアプリケー
ションサーバーに BMT トランザクションの開始と終わりを告げます。すなわち、トランザクション管理
のコードは、管理されない環境と同じになります。
// BMT idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}
トランザクション境界として Session を使いたい場合、簡単にコンテキストを伝播する機能である
getCurrentSession() があるので、 JT Aの UserT ransaction API を直接使用すべきでしょう。
160
第11章 トランザクションと並行性
// BMT idiom with getCurrentSession()
try {
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");
tx.begin();
// Do some work on Session bound to transaction
factory.getCurrentSession().load(...);
factory.getCurrentSession().persist(...);
tx.commit();
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
CMT では、トランザクション境界をセッション Bean のデプロイメントディスクリプタで定義し、プログ
ラムでは行いません。ゆえに、コードは次のように少なくなります:
// CMT idiom
Session sess = factory.getCurrentSession();
// do some work
...
In a CMT /EJB even rollback happens automatically, since an unhandled Runtim eException thrown by
a session bean method tells the container to set the global transaction to rollback. This means you do
not need to use the Hibernate Transaction API at all with BMT or CMT, and you get automatic
propagation of the "current" Session bound to the transaction.
Note that you should choose org.hibernate.transaction.JT AT ransactionFactory if you
use JT A directly (BMT ), and org.hibernate.transaction.CMT T ransactionFactory in a CMT
session bean, when you configure Hibernate's transaction factory. Remember to also set
hibernate.transaction.m anager_lookup_class. Furthermore, make sure that your
hibernate.current_session_context_class is either unset (backwards compatiblity), or set to
"jta".
getCurrentSession() オペレーションは、 JT A 環境では1つの欠点を持ちます。デフォルトで使われ
る after_statem ent コネクションリリースモードを使用する上で、警告が1つあります。 JT A 仕様の
愚かな制約のために、 scroll() または iterate() が返した、閉じられていない
ScrollableResults または Iterator インスタンスを Hibernate が自動的にクリーンアップすること
はできません。 finally ブロックの中で、 ScrollableResults.close() または
Hibernate.close(Iterator) を明示的に呼び出して、裏に潜んだデータベースカーソルを解放 しな
ければなりません。 (もちろん、多くのアプリケーションでは、 JT A か CMT コードで scroll() や
iterate() の使用を避けるのは容易です。)
11.2.3. 例外ハンドリング
Session が例外 (SQLExceptionを含む) を投げた場合、直ちに、データベーストランザクションを
ロールバックし、 Session.close() を呼び、 Session インスタンスを破棄すべきです。 Session
のいくつかのメソッドは、セッションの状態を 矛盾したまま にします。 Hibernate が投げた例外を、回
復できるものとして扱うことはできません。 finally ブロックの中で close() を呼んで、 Session
161
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
を確実に閉じてください。
T he HibernateException, which wraps most of the errors that can occur in a Hibernate persistence
layer, is an unchecked exception (it wasn't in older versions of Hibernate). In our opinion, we shouldn't
force the application developer to catch an unrecoverable exception at a low layer. In most systems,
unchecked and fatal exceptions are handled in one of the first frames of the method call stack (i.e. in
higher layers) and an error message is presented to the application user (or some other appropriate
action is taken). Note that Hibernate might also throw other unchecked exceptions which are not a
HibernateException. T hese are, again, not recoverable and appropriate action should be taken.
Hibernate は、データベースとの対話中に投げられた SQLException を JDBCException でラップし
ます。実は、例外をより意味のある JDBCException のサブクラスに変換しようと試みます。元の
SQLException は、 JDBCException.getCause() によりいつでも得られます。 Hibernate は、
SessionFactory に追加されている SQLExceptionConverter を使い、 SQLException を適当な
JDBCException サブクラスに変換します。デフォルトでは、 SQLExceptionConverter は設定され
ている SQL 方言により定義されます。一方で、独自の実装に差し替えることもできます (詳細は、
SQLExceptionConverterFactory クラスの Javadoc を参照してください)。標準的な
JDBCException のサブタイプを下記に示します。
JDBCConnectionException - 基礎となる JDBC 通信のエラーを表します。
SQLGram m arException - 発行する SQL の文法もしくは構文の問題を表します。
ConstraintViolationException - 何らかの形式の完全性制約違反を表します。
LockAcquisitionException - 要求された操作を実施するのに必要なロックレベルを得る際のエ
ラーを表します。
GenericJDBCException - 他のカテゴリに一致しなかった一般的な例外です。
11.2.4. トランザクションのタイムアウト
EJB のような管理された環境が提供するきわめて重要な特徴の1つは、トランザクションのタイムアウト
です。これは管理されていないコードには提供できません。トランザクションタイムアウトは、不品行な
トランザクションがユーザーにレスポンスを返さないまま、無期限にリソースを使い続けないことを保障
します。管理された環境 (JT A) の外では、 Hibernate はこの機能をフルに提供できません。しかしなが
ら、 Hibernate は次のようなデータアクセス操作の制御くらいはできます。データベースレベルのデッド
ロックや大きなリザルトセットを返すクエリを定義されたタイムアウトによって確実に制限します。管理
された環境では、 Hibernate はトランザクションタイムアウトを JT A に委譲します。この機能は、
Hibernate の T ransaction オブジェクトによって抽象化されています。
162
第11章 トランザクションと並行性
Session sess = factory.openSession();
try {
//set transaction timeout to 3 seconds
sess.getTransaction().setTimeout(3);
sess.getTransaction().begin();
// do some work
...
sess.getTransaction().commit()
}
catch (RuntimeException e) {
sess.getTransaction().rollback();
throw e; // or display error message
}
finally {
sess.close();
}
CMT Bean の中では setT im eout() を呼び出せないことに注意してください。トランザクションタイム
アウトは宣言的に定義されるべきです。
11.3. 楽 観 的 同 時 実 行 制 御
高い並列性と高いスケーラビリティの両方を実現するアプローチは、バージョニングを使った楽観的同時
実行制御のみです。更新の衝突を見つけるために(および、更新が失われるのを防ぐために)、バージョ
ン番号もしくはタイムスタンプを使って、バージョンをチェックします。 Hibernate は、楽観的同時実行
を行うアプリケーションコードを書くためのアプローチを3つ提供します。私たちが見せるユースケース
は、長い対話を持ちますが、バージョンチェックはまだ1つのデータベーストランザクションの中で更新
を失うことを防ぐ利点も持っています。
11.3.1. アプリケーションによるバージョンチェック
Hibernate にほとんど助けてもらわずに実装するケースです。データベースとのやり取りは、それぞれ新
しい Session の中で起こります。開発者は、すべての永続性インスタンスを操作する前に、データベー
スから再読み込みする責務があります。このアプローチでは、対話トランザクションの分離を守るため
に、アプリケーション自身がバージョンチェックを行う必要があります。このアプローチは、データベー
スアクセスの中では、最も非効率です。エンティティ EJB と最も似ているアプローチです。
// foo is an instance loaded by a previous Session
session = factory.openSession();
Transaction t = session.beginTransaction();
int oldVersion = foo.getVersion();
session.load( foo, foo.getKey() ); // load the current state
if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();
foo.setProperty("bar");
t.commit();
session.close();
T he version property is mapped using <version>, and Hibernate will automatically increment it
during flush if the entity is dirty.
Of course, if you are operating in a low-data-concurrency environment and don't require version
checking, you may use this approach and just skip the version check. In that case, last commit wins will
163
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
be the default strategy for your long conversations. Keep in mind that this might confuse the users of the
application, as they might experience lost updates without error messages or a chance to merge
conflicting changes.
確かに、マニュアルによるバージョンチェックは、些細な儀式だけで実行できますが、多くのアプリケー
ションにとって実用的ではありません。しばしば、1つのインスタンスだけでなく、修正されたオブジェ
クトの完全なグラフをチェックしなければなりません。 Hibernate は、設計パラダイムとして、拡張
Session か分離されたインスタンスを自動的にバージョンチェックします。
11.3.2. 拡張セッションと自動バージョニング
A single Session instance and its persistent instances are used for the whole conversation, known as
session-per-conversation. Hibernate checks instance versions at flush time, throwing an exception if
concurrent modification is detected. It's up to the developer to catch and handle this exception (common
options are the opportunity for the user to merge changes or to restart the business conversation with
non-stale data).
ユーザーの対話を待っているときは、 Session を基礎となる JDBC コネクションから切り離します。こ
のアプローチは、データベースアクセスの中では、最も効率的です。アプリケーションは、バージョン
チェックや分離されたインスタンスを再追加することに関心を持つ必要はありません。また、あらゆる
データベーストランザクションの中でインスタンスを再読み込みする必要はありません。
// foo is an instance loaded earlier by the old session
Transaction t = session.beginTransaction(); // Obtain a new JDBC connection, start
transaction
foo.setProperty("bar");
session.flush();
t.commit();
session.close();
// Only for last transaction in conversation
// Also return JDBC connection
// Only for last transaction in conversation
T he foo object still knows which Session it was loaded in. Beginning a new database transaction on
an old session obtains a new connection and resumes the session. Committing a database transaction
disconnects a session from the JDBC connection and returns the connection to the pool. After
reconnection, to force a version check on data you aren't updating, you may call Session.lock() with
LockMode.READ on any objects that might have been updated by another transaction. You don't need
to lock any data that you are updating. Usually you would set FlushMode.NEVER on an extended
Session, so that only the last database transaction cycle is allowed to actually persist all modifications
made in this conversation. Hence, only this last database transaction would include the flush()
operation, and then also close() the session to end the conversation.
ユーザーが考慮中に、格納することができないくらい Session が大きいのであれば、このパターンは問
題があります。例えば、 HttpSession は可能な限り小さく保つべきです。 Session は (強制的に)
1次キャッシュでもあり、ロードしたオブジェクトをすべて保持します。おそらく、リクエスト/レスポ
ンスのサイクルが数回であれば、この戦略が使えます。1つの対話のためだけに Session を使うべきで
す。なぜなら、すぐに新鮮でないデータを持つためです。
(Hibernate の以前のバージョンは、明示的な Session の切断と再接続が必要だったことに注意してく
ださい。これらのメソッドは非推奨になりました。なぜなら、トランザクションの開始と終了は同じ効果
があるためです。)
Also note that you should keep the disconnected Session close to the persistence layer. In other
words, use an EJB stateful session bean to hold the Session in a three-tier environment, and don't
transfer it to the web layer (or even serialize it to a separate tier) to store it in the HttpSession.
164
第11章 トランザクションと並行性
拡張セッションパターン (もしくは、 session-per-conversation ) は、自動的なカレントセッションコ
ンテキスト管理を実施するより難しい。このために、あなたは CurrentSessionContext の実装を供
給する必要があります。 Hibernate Wiki にある例を参照してください。
11.3.3. デタッチされたオブジェクトと自動バージョニング
新しい Session により、永続化ストア (訳注:DB) との対話が発生します。また一方、同じ永続性
インスタンスが、データベースとの対話ごとに再利用されます。アプリケーションは、元々は他の
Session でロードされ、デタッチされたインスタンスの状態を操作します。そして、
Session.update() もしくは、 Session.saveOrUpdate() 、 Session.m erge() を使って、それ
らのインスタンスを再追加します。
// foo is an instance loaded by a previous Session
foo.setProperty("bar");
session = factory.openSession();
Transaction t = session.beginTransaction();
session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
t.commit();
session.close();
この場合もやはり、 Hibernate はフラッシュする際に、インスタンスのバージョンをチェックします。更
新の競合が発生した場合には、例外を投げます。
オブジェクトが修正されていないことを確信している場合は、 update() の代わりに、
LockMode.READ を使って、 lock() を呼び出すこともできます (すべてのキャッシュを迂回し、バー
ジョンチェックを実施します)。
11.3.4. 自動バージョニングのカスタマイズ
You may disable Hibernate's automatic version increment for particular properties and collections by
setting the optim istic-lock mapping attribute to false. Hibernate will then no longer increment
versions if the property is dirty.
Legacy database schemas are often static and can't be modified. Or, other applications might also
access the same database and don't know how to handle version numbers or even timestamps. In both
cases, versioning can't rely on a particular column in a table. T o force a version check without a version
or timestamp property mapping, with a comparison of the state of all fields in a row, turn on
optim istic-lock="all" in the <class> mapping. Note that this concepetually only works if
Hibernate can compare the old and new state, i.e. if you use a single long Session and not sessionper-request-with-detached-objects.
Sometimes concurrent modification can be permitted as long as the changes that have been made don't
overlap. If you set optim istic-lock="dirty" when mapping the <class>, Hibernate will only
compare dirty fields during flush.
In both cases, with dedicated version/timestamp columns or with full/dirty field comparison, Hibernate
uses a single UPDAT E statement (with an appropriate WHERE clause) per entity to execute the version
check and update the information. If you use transitive persistence to cascade reattachment to
associated entities, Hibernate might execute uneccessary updates. T his is usually not a problem, but on
update triggers in the database might be executed even when no changes have been made to detached
instances. You can customize this behavior by setting select-before-update="true" in the
<class> mapping, forcing Hibernate to SELECT the instance to ensure that changes did actually occur,
before updating the row.
165
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
11.4. 悲 観 的 ロ ッ ク
ユーザーがロック戦略に悩むのに多くの時間を費やすことを意図していません。通常は、 JDBC コネク
ションに分離レベルを指定し、単にデータベースにすべての仕事をさせれば十分です。しかしながら、高
度なユーザーは、排他的な悲観的ロックを獲得することか、新しいトランザクションが開始される際に
ロックを再獲得することをときどき望むかもしれません。
Hibernate はいつもデータベースのロックの仕組みを使います。メモリ内のオブジェクトを決してロック
しません。
LockMode クラスは、 Hibernate が獲得できる異なるロックレベルを定義します。以下の仕組みにより、
ロックを獲得できます。
LockMode.WRIT E は、 Hibernate が行を更新もしくは挿入する際に自動的に得られます。
LockMode.UPGRADE は、データベースでサポートされている文法 SELECT ... FOR UPDAT E を
使った、明示的なユーザー要求により得られるかもしれません。
LockMode.UPGRADE_NOWAIT は、 Oracle で SELECT ... FOR UPDAT E NOWAIT を使った、明
示的なユーザー要求により得られるかもしれません。
LockMode.READ は、 Repeatable Read もしくは Serializable の分離レベルで、データを読んだ際に
自動的に得られます。おそらく、明示的なユーザー要求により、再取得されます。
LockMode.NONE は、ロックしないことを表します。 T ransaction の終わりに、すべてのオブ
ジェクトはこのロックモードに切り替わります。 update() や saveOrUpdate() を呼び出すことに
よって、セッションに関連付けられたオブジェクトも、このロックモードで出発します。
T he "explicit user request" is expressed in one of the following ways:
LockMode を指定した Session.load() の呼び出し。
Session.lock() の呼び出し。
Query.setLockMode() の呼び出し。
UPGRADE もしくは UPGRADE_NOWAIT が指定された Session.load() が呼び出され、かつ要求された
オブジェクトがセッションによってまだロードされていなかった場合は、 SELECT ... FOR UPDAT E
を使って、オブジェクトがロードされます。 load() で呼び出されたオブジェクトが、要求されているよ
り制限が少ないロックですでにロードされていた場合は、 Hibernate はそのオブジェクトのために、
lock() を呼び出します。
指定されたロックモードが READ もしくは、 UPGRADE 、 UPGRADE_NOWAIT だった場合、
Session.lock() は、バージョン番号のチェックを実施します。 (UPGRADE もしくは
UPGRADE_NOWAIT の場合、 SELECT ... FOR UPDAT E が使われます。)
データベースが要求されたロックモードをサポートしていない場合、 Hibernate は(例外を投げる代わり
に、)適切な代わりのモードを使います。これは、アプリケーションがポータブルであることを保証しま
す。
11.5. コ ネ ク シ ョ ン 開 放 モ ー ド
Hibernate のレガシー(2.x)の JDBC コネクション管理に関する振る舞いは、最初に必要とした際に
Session がコネクションを得るというものでした。そして、セッションが閉じられるまで、そのコネク
ションを保持しました。 Hibernate 3.x は、セッションに JDBC コネクションをどのように制御するかを
伝えるコネクション開放モードという概念を導入しました。以降の議論は、構成された
ConnectionProvider を通して提供されるコネクションに適切であることに注意してください。異な
る開放モードは、 org.hibernate.ConnectionReleaseMode に列挙された値により確認されま
す。
166
第11章 トランザクションと並行性
ON_CLOSE - 本質的に上記で述べたレガシーの振る舞いです。 Hibernate セッションは最初に JDBC
アクセスを実行する必要がある際にコネクションを得ます。そして、セッションが閉じられるまで、
コネクションを保持します。
AFT ER_T RANSACT ION - org.hibernate.T ransaction が完了した後、コネクションを開放しま
す。
AFT ER_ST AT EMENT (積極的な開放とも呼ばれる) - すべてのステートメントがそれぞれ実行され
た後、コネクションが開放されます。ステートメントがセッションに関連するリソースを開いたまま
にする場合は、この積極的な開放はスキップされます。今のところ、これが起こるのは
org.hibernate.ScrollableResults が使われる場合のみです。
コンフィグレーションパラメータの hibernate.connection.release_m ode は、使用する開放モー
ドを指定するために使います。指定できる値は次の通りです:
auto (デフォルト) - これを選択すると
org.hibernate.transaction.T ransactionFactory.getDefaultReleaseMode() メソッ
ドによって返される開放モードに委譲されます。このメソッドは、 JT AT ransactionFactory には
ConnectionReleaseMode.AFT ER_ST AT EMENT を返し、 JDBCT ransactionFactory には
ConnectionReleaseMode.AFT ER_T RANSACT ION を返します。このデフォルトの振る舞いを変えて
うまくいった試しがありません。それは、この設定値が原因で起こる障害は、ユーザーコードの中で
バグや間違った条件になりやすいからです。
on_close - ConnectionReleaseMode.ON_CLOSE を使います。この設定は後方互換のために残され
ていますが、使わないことを強く勧めます。
after_transaction - ConnectionReleaseMode.AFT ER_T RANSACT ION を使います。この設定は
JT A 環境の中では使うべきではありません。 ConnectionReleaseMode.AFT ER_T RANSACT ION を指
定し、自動コミットモードの中では、開放モードが AFT ER_ST AT EMENT であるかのように、コネク
ションは開放されることに注意してください。
after_statem ent - ConnectionReleaseMode.AFT ER_ST AT EMENT を使います。さらに、設定さ
れた ConnectionProvider は、この設定 (supportsAggressiveRelease()) をサポートするか
どうかを調べるために使用します。もしそうでない場合、開放モードは
ConnectionReleaseMode.AFT ER_T RANSACT ION にリセットされます。この設定は次の環境でのみ
安全です。それは、 ConnectionProvider.getConnection() を呼び出すたびに基盤となる
JDBC コネクションが同じものを取得できるか、同じコネクションが得られることが問題とならない自
動コミット環境の中です。
167
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 12章 インターセプタとイベント
アプリケーションが Hibernate の内部で発生するイベントに対応できると役に立つことがあります。ある
種の一般的な機能を実装できるようになり、また Hibernate の機能を拡張することもできるようになりま
す。
12.1. イ ン タ ー セ プ タ
Interceptor インターフェースを使って、セッションからアプリケーションへコールバックをすること
ができます。これにより永続オブジェクトの保存、更新、削除、読み込みの前に、アプリケーションがプ
ロパティを検査したり操作したりできるようになります。これは監査情報の追跡に利用できます。下の例
で Interceptor は Auditable が作成されると自動的に createT im estam p を設定し、
Auditable が更新されると自動的に lastUpdateT im estam p プロパティを更新します。
Interceptor を直接実装したり、 (さらによいのは) Em ptyInterceptor を拡張したりできます。
168
第12章 インターセプタとイベント
package org.hibernate.test;
import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;
public class AuditInterceptor extends EmptyInterceptor {
private int updates;
private int creates;
private int loads;
public void onDelete(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
// do nothing
}
public boolean onFlushDirty(Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types) {
if ( entity instanceof Auditable ) {
updates++;
for ( int i=0; i < propertyNames.length; i++ ) {
if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
currentState[i] = new Date();
return true;
}
}
}
return false;
}
public boolean onLoad(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if ( entity instanceof Auditable ) {
loads++;
}
return false;
}
public boolean onSave(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
169
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
if ( entity instanceof Auditable ) {
creates++;
for ( int i=0; i<propertyNames.length; i++ ) {
if ( "createTimestamp".equals( propertyNames[i] ) ) {
state[i] = new Date();
return true;
}
}
}
return false;
}
public void afterTransactionCompletion(Transaction tx) {
if ( tx.wasCommitted() ) {
System.out.println("Creations: " + creates + ", Updates: " + updates,
"Loads: " + loads);
}
updates=0;
creates=0;
loads=0;
}
}
インターセプタには二種類あります: Session スコープのものと SessionFactory スコープのもので
す。
Session スコープのインターセプタは、セッションをオープンするときに指定します。 Interceptor
を引数に取る SessionFactory.openSession() のオーバーロードメソッドの一つを使います。
Session session = sf.openSession( new AuditInterceptor() );
SessionFactory スコープのインターセプタは Configuration オブジェクトを使って登録します。
これは SessionFactory の構築よりも優先されます。この場合、提供されるインターセプタは
SessionFactory からオープンされたすべてのセッションに適用されます。これは使用するインターセ
プタを明示的に指定してセッションをオープンしない限り、そうなります。 SessionFactory スコープ
のインターセプタはスレッドセーフでなければなりません。複数のセッションが (潜在的に) このイン
ターセプタを同時並行で使用することになるため、セッション固有の状態を格納しないように気をつけて
ください。
new Configuration().setInterceptor( new AuditInterceptor() );
12.2. イ ベ ン ト シ ス テ ム
永続化層で特定のイベントに対応しなければならない場合、 Hibernate3 の イベント アーキテクチャを使
うこともできます。イベントシステムはインターセプタと一緒に使うか、またはインターセプタの代わり
として使うことができます。
本質的に Session インターフェースのすべてのメソッドは、1個のイベントと相互に関連します。例え
ば LoadEvent 、 FlushEvent などがあります (定義済みのイベント型の一覧については、 XML 設定
ファイルの DT D や org.hibernate.event パッケージを調べてください) 。リクエストがこれらのメ
ソッドの1つから作られるとき、 Hibernate の Session は適切なイベントを生成し、そのイベント型に
設定されたイベントリスナに渡します。すばらしいことに、これらのリスナはそのメソッドと同じ処理を
実装します。とはいえ、リスナインターフェースの一つを自由にカスタム実装できます (つまり、
LoadEvent は登録された LoadEventListener インターフェースの実装により処理されます)。その
170
第12章 インターセプタとイベント
場合、その実装には Session から作られたどのような load() リクエストをも処理する責任がありま
す。
リスナは事実上シングルトンであると見なせます。つまりリスナはリクエスト間で共有されるため、イン
スタンス変数として状態を保持するべきではないということです。
A custom listener should implement the appropriate interface for the event it wants to process and/or
extend one of the convenience base classes (or even the default event listeners used by Hibernate outof-the-box as these are declared non-final for this purpose). Custom listeners can either be registered
programmatically through the Configuration object, or specified in the Hibernate configuration XML
(declarative configuration through the properties file is not supported). Here's an example of a custom
load event listener:
public class MyLoadListener implements LoadEventListener {
// this is the single method defined by the LoadEventListener interface
public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
throws HibernateException {
if ( !MySecurity.isAuthorized( event.getEntityClassName(),
event.getEntityId() ) ) {
throw MySecurityException("Unauthorized access");
}
}
}
デフォルトリスナ以外のリスナを使うには、 Hibernate への設定も必要です:
<hibernate-configuration>
<session-factory>
...
<event type="load">
<listener class="com.eg.MyLoadListener"/>
<listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
</event>
</session-factory>
</hibernate-configuration>
またその他に、プログラムで登録する方法もあります:
Configuration cfg = new Configuration();
LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener()
};
cfg.EventListeners().setLoadEventListeners(stack);
Listeners registered declaratively cannot share instances. If the same class name is used in multiple
<listener/> elements, each reference will result in a separate instance of that class. If you need the
capability to share listener instances between listener types you must use the programmatic registration
approach.
なぜインターフェースを実装して、特化した型を設定時に指定するのでしょうか?リスナの実装クラス
に、複数のイベントリスナインターフェースを実装できるからです。登録時に追加で型を指定すること
で、カスタムリスナの on/off を設定時に簡単に切り替えられます。
12.3. Hibernate の 宣 言 的 な セ キ ュ リ テ ィ
一般的に Hibernate アプリケーションの宣言的なセキュリティは、セッションファサード層で管理しま
171
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
す。現在、 Hiberenate3 は JACC で許可し、さらに JAAS で認証したアクションを許しています。これは
イベントアーキテクチャの最上位に組み込まれているオプションの機能です。
まず最初に、適切なイベントリスナを設定して JAAS 認証を使えるようにしなければなりません。
<listener type="pre-delete"
class="org.hibernate.secure.JACCPreDeleteEventListener"/>
<listener type="pre-update"
class="org.hibernate.secure.JACCPreUpdateEventListener"/>
<listener type="pre-insert"
class="org.hibernate.secure.JACCPreInsertEventListener"/>
<listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>
Note that <listener type="..." class="..."/> is just a shorthand for <event
type="..."><listener class="..."/></event> when there is exactly one listener for a
particular event type.
次に、同じく hibernate.cfg.xm l でロールにパーミッションを与えてください:
<grant role="admin" entity-name="User" actions="insert,update,read"/>
<grant role="su" entity-name="User" actions="*"/>
このロール名は使用する JACC プロバイダに理解されるロールです。
172
第13章 バッチ処理
第 13章 バッチ処理
Hibernate を使ってデータベースに100,000行を挿入する愚直な方法は、このようなものです:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
}
tx.commit();
session.close();
T his would fall over with an OutOfMem oryException somewhere around the 50 000th row. T hat's
because Hibernate caches all the newly inserted Custom er instances in the session-level cache.
In this chapter we'll show you how to avoid this problem. First, however, if you are doing batch
processing, it is absolutely critical that you enable the use of JDBC batching, if you intend to achieve
reasonable performance. Set the JDBC batch size to a reasonable number (say, 10-50):
hibernate.jdbc.batch_size 20
Note that Hibernate disables insert batching at the JDBC level transparently if you use an identiy
identifier generator.
また二次キャッシュが全く効かないプロセスで、このような作業をしたいと思うかもしれません:
hibernate.cache.use_second_level_cache false
しかし、これは絶対に必要というわけではありません。なぜなら明示的に CacheMode を設定して、二次
キャッシュとの相互作用を無効にすることができるからです。
13.1. バ ッ チ 挿 入
新しいオブジェクトを永続化するとき、一次キャッシュのサイズを制限するため、セッションを
flush() して clear() しなければなりません。
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
if ( i % 20 == 0 ) { //20, same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
13.2. バ ッ チ 更 新
173
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
データを復元したり更新したりするには同じアイディアを適用します。それに加えて、データの行を多く
返すクエリに対して有効なサーバーサイドのカーソルの利点を生かしたければ scroll() を使う必要が
あります。
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
ScrollableResults customers = session.getNamedQuery("GetCustomers")
.setCacheMode(CacheMode.IGNORE)
.scroll(ScrollMode.FORWARD_ONLY);
int count=0;
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
if ( ++count % 20 == 0 ) {
//flush a batch of updates and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
13.3. StatelessSession イ ン タ ー フ ェ ー ス
Alternatively, Hibernate provides a command-oriented API that may be used for streaming data to and
from the database in the form of detached objects. A StatelessSession has no persistence context
associated with it and does not provide many of the higher-level lifecycle semantics. In particular, a
stateless session does not implement a first-level cache nor interact with any second-level or query
cache. It does not implement transactional write-behind or automatic dirty checking. Operations
performed using a stateless session do not ever cascade to associated instances. Collections are
ignored by a stateless session. Operations performed via a stateless session bypass Hibernate's event
model and interceptors. Stateless sessions are vulnerable to data aliasing effects, due to the lack of a
first-level cache. A stateless session is a lower-level abstraction, much closer to the underlying JDBC.
StatelessSession session = sessionFactory.openStatelessSession();
Transaction tx = session.beginTransaction();
ScrollableResults customers = session.getNamedQuery("GetCustomers")
.scroll(ScrollMode.FORWARD_ONLY);
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
session.update(customer);
}
tx.commit();
session.close();
このコード例では、クエリが返す Custom er インスタンスは即座に (セッションから) 分離されること
に注意してください。これは、どのような永続コンテキストとも決して関連しません。
StatelessSession インターフェースで定義されている insert(), update() と delete() の操作
は、低レベルの直接的なデータベース操作と考えられます。結果として、 SQL の INSERT , UPDAT E ま
たは DELET E がそれぞれ即座に実行されます。このように、これらは Session インターフェースで定義
174
第13章 バッチ処理
されている save(), saveOrUpdate() と delete() とは非常に異なる意味を持ちます。
13.4. DML ス タ イ ル の 操 作
As already discussed, automatic and transparent object/relational mapping is concerned with the
management of object state. T his implies that the object state is available in memory, hence manipulating
(using the SQL Data Manipulation Language (DML) statements: INSERT , UPDAT E, DELET E) data
directly in the database will not affect in-memory state. However, Hibernate provides methods for bulk
SQL-style DML statement execution which are performed through the Hibernate Query Language (14
章HQL: Hibernate クエリ言語 HQL).
UPDAT E と DELET E 文の疑似構文は: ( UPDAT E | DELET E ) FROM? エンティティ名 (WHERE 条
件節 )? です。注意すべき点がいくつかあります:
from 節において、 FROM キーワードはオプションです。
from 節では単一のエンティティ名だけが可能で、任意で別名を付けることができます。エンティティ
名に別名が与えられると、どのようなプロパティ参照も、その別名を使って修飾しなければなりませ
ん。もしエンティティ名に別名が与えられなければ、どのようなプロパティ参照も修飾してはなりま
せん。
No 「結合構文の形式」 joins (either implicit or explicit) can be specified in a bulk HQL query. Subqueries may be used in the where-clause; the subqueries, themselves, may contain joins.
where 節はオプションです。
As an example, to execute an HQL UPDAT E, use the Query.executeUpdate() method (the method is
named for those familiar with JDBC's PreparedStatem ent.executeUpdate()):
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlUpdate = "update Customer c set c.name = :newName where c.name =
:oldName";
// or String hqlUpdate = "update Customer set name = :newName where name =
:oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
HQL UPDAT E statements, by default do not effect the 「version(オプション)」 version or the
「timestamp(オプション)」 timestamp property values for the affected entities; this is in keeping with
the EJB3 specification. However, you can force Hibernate to properly reset the version or tim estam p
property values through the use of a versioned update. T his is achieved by adding the VERSIONED
keyword after the UPDAT E keyword.
175
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlVersionedUpdate = "update versioned Customer set name = :newName where
name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
カスタムバージョン型(org.hibernate.usertype.UserVersionT ype)は update versioned
文と一緒に使えないことに注意してください。
HQL の DELET E を実行するには、同じ Query.executeUpdate() メソッドを使ってください:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete Customer c where c.name = :oldName";
// or String hqlDelete = "delete Customer where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
T he int value returned by the Query.executeUpdate() method indicate the number of entities
effected by the operation. Consider this may or may not correlate to the number of rows effected in the
database. An HQL bulk operation might result in multiple actual SQL statements being executed, for
joined-subclass, for example. T he returned number indicates the number of actual entities affected by
the statement. Going back to the example of joined-subclass, a delete against one of the subclasses
may actually result in deletes against not just the table to which that subclass is mapped, but also the
"root" table and potentially joined-subclass tables further down the inheritence hierarchy.
INSERT 文の疑似構文は: INSERT INT O エンティティ名プロパティリスト select 文 です。注意
すべき点がいくつかあります:
INSERT INT O ... SELECT ... の形式だけがサポートされています。 INSERT INT O ... VALUES ... の形式
はサポートされていません。
プロパティリストは、 SQL の INSERT 文における カラムの仕様 に類似しています。継承のマッピ
ングに含まれるエンティティに対して、クラスレベルで直接定義されたプロパティだけが、プロパ
ティリストに使えます。スーパークラスのプロパティは認められず、サブクラスのプロパティは効果
がありません。言い換えると INSERT 文は、本質的にポリモーフィックではありません。
select 文の返り値の型が insert 文が期待する型とマッチしていれば、その select 文は妥当な HQL
select クエリとなりえます。現在このチェックをデータベースへ任せるのではなく、クエリのコンパ
イル時にチェックします。このことは、 equal とは違い、 Hibernate の T ype 間の equivalent に関す
る問題を引き起こすことに注意してください。これは org.hibernate.type.DateT ype として定
義されたプロパティと、 org.hibernate.type.T im estam pT ype として定義されたプロパティの
間のミスマッチの問題を引き起こします。データベースがそれらを区別できなくても、変換すること
ができても、この問題は発生します。
For the id property, the insert statement gives you two options. You can either explicitly specify the id
property in the properties_list (in which case its value is taken from the corresponding select
expression) or omit it from the properties_list (in which case a generated value is used). T his later
176
第13章 バッチ処理
option is only available when using id generators that operate in the database; attempting to use this
option with any "in memory" type generators will cause an exception during parsing. Note that for the
purposes of this discussion, in-database generators are considered to be
org.hibernate.id.SequenceGenerator (and its subclasses) and any implementors of
org.hibernate.id.PostInsertIdentifierGenerator. T he most notable exception here is
org.hibernate.id.T ableHiLoGenerator, which cannot be used because it does not expose
a selectable way to get its values.
version や tim estam p としてマッピングされるプロパティに対して、 insert 文には二つの選択肢
があります。プロパティリストで明示的にプロパティを指定するか(この場合、対応する select 式か
ら値が取られます)、プロパティリストから除外するか(この場合、
org.hibernate.type.VersionT ype で定義された シード値 が使われます)のいずれかです。
HQL の INSERT 文の実行例です:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name
from Customer
c where ...";
int createdEntities = s.createQuery( hqlInsert )
.executeUpdate();
tx.commit();
session.close();
177
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 14章 HQL: Hibernate クエリ言語
Hibernate is equipped with an extremely powerful query language that (quite intentionally) looks very
much like SQL. But don't be fooled by the syntax; HQL is fully object-oriented, understanding notions like
inheritence, polymorphism and association.
14.1. 大 文 字 と 小 文 字 の 区 別
クエリは Java のクラス名とプロパティ名を除いて大文字、小文字を区別しません。従って SeLeCT は
sELEct と同じで、かつ SELECT とも同じですが org.hibernate.eg.FOO は
org.hibernate.eg.Foo とは違い、かつ foo.barSet は foo.BARSET とも違います。
このマニュアルでは小文字の HQL キーワードを使用します。大文字のキーワードのクエリの方が読みや
すいと感じるユーザーもいると思います。ですが、 Java コード内に埋め込まれたときには見づらいと思
います。
14.2. from 節
もっとも単純な Hibernate クエリは次の形式です:
from eg.Cat
which simply returns all instances of the class eg.Cat. We don't usually need to qualify the class name,
since auto-im port is the default. So we almost always just write:
from Cat
ほとんどの場合クエリのほかの部分で Cat を参照するので、 別名 を割り当てる必要があるでしょ
う。
from Cat as cat
このクエリでは Cat インスタンスに cat という別名を付けています。そのため、後でこのクエリ内で、
この別名を使うことができます。 as キーワードはオプションです。つまりこのように書くこともできま
す:
from Cat cat
Multiple classes may appear, resulting in a cartesian product or "cross" join.
from Formula, Parameter
from Formula as form, Parameter as param
ローカル変数の Java のネーミング基準と一致した、頭文字に小文字を使ったクエリの別名を付けること
はいい習慣です (例えば dom esticCat)。
14.3. 関 連 と 結 合
関連するエンティティあるいは値コレクションの要素にも、 結合 を使って別名を割り当てることが出来
ます。
178
第14章 HQL: Hibernate クエリ言語
from Cat as cat
inner join cat.mate as mate
left outer join cat.kittens as kitten
from Cat as cat left join cat.mate.kittens as kittens
from Formula form full join form.parameter param
サポートしている結合のタイプは ANSI SQL と同じです。
inner join
left outer join
right outer join
full join (たいていの場合使いづらい)
inner join、 left outer join、 right outer join には省略形を使うこともできます。
from Cat as cat
join cat.mate as mate
left join cat.kittens as kitten
HQL の with キーワードを使うと、結合条件を付け加えることができます。
from Cat as cat
left join cat.kittens as kitten
with kitten.bodyWeight > 10.0
In addition, a "fetch" join allows associations or collections of values to be initialized along with their
parent objects, using a single select. T his is particularly useful in the case of a collection. It effectively
overrides the outer join and lazy declarations of the mapping file for associations and collections. See
「フェッチ戦略」 for more information.
from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens
結合によるフェッチは関連するオブジェクトが where 節 (または他のどんな節でも) で使われてはならな
いので、通常別名を割り当てる必要がありません。また関連オブジェクトは問い合わせ結果として直接返
されません。代わりに親オブジェクトを通してアクセスできます。コレクションを再帰的に結合フェッチ
する場合のみ、別名が必要になります:
from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens child
left join fetch child.kittens
Note that the fetch construct may not be used in queries called using iterate() (though scroll()
can be used). Nor should fetch be used together with setMaxResults() or setFirstResult() as
these operations are based on the result rows, which usually contain duplicates for eager collection
fetching, hence, the number of rows is not what you'd expect. Nor may fetch be used together with an
ad hoc with condition. It is possible to create a cartesian product by join fetching more than one
collection in a query, so take care in this case. Join fetching multiple collection roles also sometimes
179
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
gives unexpected results for bag mappings, so be careful about how you formulate your queries in this
case. Finally, note that full join fetch and right join fetch are not meaningful.
もしプロパティレベルの遅延フェッチを使う場合(内部的にバイトコード処理をする場合)、 fetch
all properties を使うことで Hibernate に遅延プロパティを速やかに(最初のクエリで)フェッチさ
せることができます。
from Document fetch all properties order by name
from Document doc fetch all properties where lower(doc.name) like '%cats%'
14.4. 結 合 構 文 の 形 式
HQL は2つの関連結合形式をサポートします: 暗黙的 と 明示的 。
これまでのセクションでお見せした使い方はすべて 明示的な 形式で、 from 節で明示的に join キーワー
ドを使っています。この形式をおすすめします。
T he im plicit form does not use the join keyword. Instead, the associations are "dereferenced" using
dot-notation. im plicit joins can appear in any of the HQL clauses. im plicit join result in inner joins
in the resulting SQL statement.
from Cat as cat where cat.mate.name like '%s%'
14.5. 識 別 子 プ ロ パ テ ィ の 参 照
T here are, generally speaking, 2 ways to refer to an entity's identifier property:
特別なプロパティ (小文字) id は、 id と名付けられた非識別子プロパティを定義しないエンティティ
を与えられた エンティティの識別子プロパティを参照するのに使用されます。
もしエンティティが名付けられた識別子プロパティを定義したら、そのプロパティ名を使用できま
す。
複合識別子プロパティへの参照は同じ命名ルールに従います。もしエンティティが id と名付けられた非識
別子プロパティを持っていたら、複合識別子プロパティはその定義された名前で参照することができま
す。そうでないと、特別な id プロパティは、識別子プロパティを参照するのに使用されます。
注記: これは、バージョン 3.2.2 から大幅に変更しました。前バージョンでは、 id は、その実際の名前に
関係なく 常に 識別子プロパティを参照していました。その結果、 id と名付けられた非識別子プロパティ
は、 Hibernate で決して参照されませんでした。
14.6. Select 節
select 節は以下のようにどのオブジェクトと属性をクエリリザルトセットに返すかを選択します:
select mate
from Cat as cat
inner join cat.mate as mate
上記のクエリは他の Cat の m ate を選択します。実際には次のように、より簡潔に表現できます:
180
第14章 HQL: Hibernate クエリ言語
select cat.mate from Cat cat
クエリはコンポーネント型のプロパティを含む、あらゆる値型のプロパティも返せます:
select cat.name from DomesticCat cat
where cat.name like 'fri%'
select cust.name.firstName from Customer as cust
クエリは複数のオブジェクトと (または) プロパティを Object[] 型の配列として返せます。
select mother, offspr, mate.name
from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr
もしくは List として、
select new list(mother, offspr, mate.name)
from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr
または、タイプセーフな Java オブジェクトを返せます。
select new Family(mother, mate, offspr)
from DomesticCat as mother
join mother.mate as mate
left join mother.kittens as offspr
あるいは Fam ily クラスが適切なコンストラクタを持っているとするならば、
select 節に as を使って別名をつけることもできます。
select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
from Cat cat
select new m ap と一緒に使うときに最も役立ちます:
select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n )
from Cat cat
このクエリは別名から select した値へ Map を返します。
14.7. 集 約 関 数
HQL のクエリはプロパティの集約関数の結果も返せます:
select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
from Cat cat
サポートしている集約関数は以下のものです。
181
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
avg(...), sum (...), m in(...), m ax(...)
count(* )
count(...), count(distinct ...), count(all...)
select 節において算術操作、連結と承認された SQL 関数を使うことができます:
select cat.weight + sum(kitten.weight)
from Cat cat
join cat.kittens kitten
group by cat.id, cat.weight
select firstName||' '||initial||' '||upper(lastName) from Person
SQL と同じ意味を持つ distinct と all キーワードを使うことができます。
select distinct cat.name from Cat cat
select count(distinct cat.name), count(cat) from Cat cat
14.8. ポ リ モ ー フ ィ ッ ク な ク エ リ
次のようなクエリ:
from Cat as cat
Cat インスタンスだけではなく、 Dom esticCat のようなサブクラスも返されます。 Hibernate クエリ
は どんな Java クラスやインターフェースも from 節に入れることができます。クエリはそのクラスを拡
張した、もしくはインターフェースを実装した全ての永続クラスを返します。次のクエリは永続オブジェ
クトをすべて返します:
from java.lang.Object o
Nam ed インターフェースは様々な永続クラスによって実装されます。:
from Named n, Named m where n.name = m.name
Note that these last two queries will require more than one SQL SELECT . T his means that the order
by clause does not correctly order the whole result set. (It also means you can't call these queries using
Query.scroll().)
14.9. where 節
where 節は返されるインスタンスのリストを絞ることができます。もし別名がない場合、名前でプロパ
ティを参照します。
from Cat where name='Fritz'
もし別名がある場合、修飾名を使ってください:
from Cat as cat where cat.name='Fritz'
182
第14章 HQL: Hibernate クエリ言語
returns instances of Cat named 'Fritz'.
select foo
from Foo foo, Bar bar
where foo.startDate = bar.date
上の HQL は、 Foo の startDate プロパティと等しい date プロパティを持った bar インスタンスが
存在する、すべての Foo インスタンスを返します。コンパウンドパス式)は where 節を非常に強力にし
ます。注目:
from Cat cat where cat.mate.name is not null
このクエリはテーブル結合(内部結合)を持つ SQL クエリに変換されます。その代わりに以下のように
書くと、
from Foo foo
where foo.bar.baz.customer.address.city is not null
もし上のクエリを記述したらクエリ内に4つのテーブル結合を必要とする SQL クエリに変換されます。
= 演算子は以下のように、プロパティだけでなくインスタンスを比較するためにも使われます。:
from Cat cat, Cat rival where cat.mate = rival.mate
select cat, mate
from Cat cat, Cat mate
where cat.mate = mate
T he special property (lowercase) id may be used to reference the unique identifier of an object. See
「識別子プロパティの参照」 for more information.
from Cat as cat where cat.id = 123
from Cat as cat where cat.mate.id = 69
2番目のクエリは効率的です。テーブル結合が必要ありません。
Properties of composite identifiers may also be used. Suppose Person has a composite identifier
consisting of country and m edicareNum ber. Again, see 「識別子プロパティの参照」 for more
information regarding referencing identifier properties.
from bank.Person person
where person.id.country = 'AU'
and person.id.medicareNumber = 123456
from bank.Account account
where account.owner.id.country = 'AU'
and account.owner.id.medicareNumber = 123456
繰り返しますが、2番目のクエリにはテーブル結合が必要ありません。
同様に class は特別なプロパティであり、ポリモーフィックな永続化におけるインスタンスの
discriminator 値にアクセスします。 where 節に埋め込まれた Java のクラス名はその discriminator 値に
変換されます。
183
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
from Cat cat where cat.class = DomesticCat
You may also use components or composite user types, or properties of said component types. See
「コンポーネント」 for more details.
An "any" type has the special properties id and class, allowing us to express a join in the following
way (where AuditLog.item is a property mapped with <any>).
from AuditLog log, Payment payment
where log.item.class = 'Payment' and log.item.id = payment.id
log.item .class と paym ent.class が上記のクエリ中で全く異なるデータベースカラムの値を参照
するということに注意してください。
14.10. Expressions 式
SQL の where 節で記述することが出来る式のほとんどを HQL でも記述できます:
算術演算子:+, -, * , /
binary comparison operators =, >=, <=, <>, !=, like
論理演算子:and, or, not
グループ分けを表す括弧:( )
in, not in, between, is null, is not null, is em pty, is not em pty, m em ber of and
not m em ber of
"Simple" case, case ... when ... then ... else ... end, and "searched" case, case
when ... then ... else ... end
ストリングの連結 ...||... または concat(...,...)
current_date(), current_tim e(), current_tim estam p()
second(...), m inute(...), hour(...), day(...), m onth(...), year(...),
EJB-QL 3.0 で定義されている関数や演算子: substring(), trim (), lower(), upper(),
length(), locate(), abs(), sqrt(), bit_length(), m od()
coalesce() と nullif()
数字や時間の値を String にコンバートする str()
2番目の引数が Hibernate 型の名前である cast(... as ...) と extract(... from ...)。ただ
し使用するデータベースが ANSI cast() と extract() をサポートする場合に限ります。
結合したインデックス付きのコレクションの別名に適用される HQL の index() 関数。
コレクション値のパス式を取る HQL 関数: size(), m inelem ent(), m axelem ent(),
m inindex(), m axindex() 。 som e, all, exists, any, in を使って修飾することができる
特別な elem ents() と indices 関数と一緒に使います。
sign()、 trunc()、 rtrim ()、 sin() のようなデータベースがサポートする SQL スカラ関数。
JDBC スタイルの位置パラメータ ?
名前付きパラメータ: :nam e, :start_date, :x1
SQL literals 'foo', 69, 6.66E+2, '1970-01-01 10:00:01.0'
Java の public static final 定数: eg.Color.T ABBY
in と between は以下のように使用できます:
184
第14章 HQL: Hibernate クエリ言語
from DomesticCat cat where cat.name between 'A' and 'B'
from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )
また、否定形で記述することもできます。
from DomesticCat cat where cat.name not between 'A' and 'B'
from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )
同様に is null や is not null は null 値をテストするために使用できます。
Hibernate 設定ファイルで HQL query substitutions を定義すれば、 boolean 値を式の中で簡単に使用で
きます:
<property name="hibernate.query.substitutions">true 1, false 0</property>
こうすることで下記の HQL を SQL に変換するときに true 、 false キーワードは 1 、 0 に置き換えら
れます:
from Cat cat where cat.alive = true
特別なプロパティ size、または特別な関数 size() を使ってコレクションのサイズをテストできます:
from Cat cat where cat.kittens.size > 0
from Cat cat where size(cat.kittens) > 0
インデックス付きのコレクションでは、 m inindex と m axindex 関数を使って、インデックスの最小値
と最大値を参照できます。同様に、 m inelem ent と m axelem ent を使って、基本型のコレクション要
素の最小値と最大値を参照できます。
from Calendar cal where maxelement(cal.holidays) > current_date
from Order order where maxindex(order.items) > 100
from Order order where minelement(order.items) > 10000
コレクションの要素やインデックスのセット(elem ents と indices 関数)、または副問い合わせ(後
述)の結果が受け取れるときは、 SQL 関数 any, som e, all, exists, in がサポートされます。
select mother from Cat as mother, Cat as kit
where kit in elements(foo.kittens)
select p from NameList list, Person p
where p.name = some elements(list.names)
from Cat cat where exists elements(cat.kittens)
185
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
from Player p where 3 > all elements(p.scores)
from Show show where 'fizard' in indices(show.acts)
size、 elem ents、 indices、 m inindex、 m axindex、 m inelem ent、 m axelem ent は
Hibernate3 の where 節だけで利用可能であることに注意してください。
インデックス付きのコレクション(arrays, lists, maps)の要素は、インデックスで参照できます(where
節内でのみ):
from Order order where order.items[0].id = 1234
select person from Person person, Calendar calendar
where calendar.holidays['national day'] = person.birthDay
and person.nationality.calendar = calendar
select item from Item item, Order order
where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11
select item from Item item, Order order
where order.items[ maxindex(order.items) ] = item and order.id = 11
[] 内部の式は、算術式でも構いません。
select item from Item item, Order order
where order.items[ size(order.items) - 1 ] = item
一対多関連や値のコレクションの要素に対しては、 HQL は組み込みの index() 関数も用意しています。
select item, index(item) from Order order
join order.items item
where index(item) < 5
ベースとなるデータベースがサポートしているスカラー SQL 関数が使用できます
from DomesticCat cat where upper(cat.name) like 'FRI%'
もしまだ全てを理解していないなら、下のクエリを SQL でどれだけ長く、読みづらく出来るか考えてく
ださい:
select cust
from Product prod,
Store store
inner join store.customers cust
where prod.name = 'widget'
and store.location.name in ( 'Melbourne', 'Sydney' )
and prod = all elements(cust.currentOrder.lineItems)
ヒント: 例えばこのように出来ます。
186
第14章 HQL: Hibernate クエリ言語
SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
FROM customers cust,
stores store,
locations loc,
store_customers sc,
product prod
WHERE prod.name = 'widget'
AND store.loc_id = loc.id
AND loc.name IN ( 'Melbourne', 'Sydney' )
AND sc.store_id = store.id
AND sc.cust_id = cust.id
AND prod.id = ALL(
SELECT item.prod_id
FROM line_items item, orders o
WHERE item.order_id = o.id
AND cust.current_order = o.id
)
14.11. order by 節
クエリが返す list は、返されるクラスやコンポーネントの任意の属性によって並べ替えられます:
from DomesticCat cat
order by cat.name asc, cat.weight desc, cat.birthdate
オプションの asc と desc はそれぞれ昇順か降順の整列を示します。
14.12. group by 節
集約値を返すクエリは、返されるクラスやコンポーネントの任意のプロパティによってグループ化できま
す:
select cat.color, sum(cat.weight), count(cat)
from Cat cat
group by cat.color
select foo.id, avg(name), max(name)
from Foo foo join foo.names name
group by foo.id
having 節も使えます。
select cat.color, sum(cat.weight), count(cat)
from Cat cat
group by cat.color
having cat.color in (eg.Color.TABBY, eg.Color.BLACK)
もし使用するデータベースがサポートしているなら、 having と order by 節で SQL 関数と集約関数が
使えます(例えば MySQL にはありません)。
187
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
select cat
from Cat cat
join cat.kittens kitten
group by cat.id, cat.name, cat.other, cat.properties
having avg(kitten.weight) > 100
order by count(kitten) asc, sum(kitten.weight) desc
Note that neither the group by clause nor the order by clause may contain arithmetic expressions.
Also note that Hibernate currently does not expand a grouped entity, so you can't write group by cat
if all properties of cat are non-aggregated. You have to list all non-aggregated properties explicitly.
14.13. 副 問 い 合 わ せ
サブセレクトをサポートするデータベースのため、 Hibernate は副問い合わせをサポートしています。副
問い合わせは括弧で囲まなければなりません( SQL の集約関数呼び出しによる事が多いです)。関連副
問い合わせ (外部クエリ中の別名を参照する副問い合わせのこと) さえ許可されます。
from Cat as fatcat
where fatcat.weight > (
select avg(cat.weight) from DomesticCat cat
)
from DomesticCat as cat
where cat.name = some (
select name.nickName from Name as name
)
from Cat as cat
where not exists (
from Cat as mate where mate.mate = cat
)
from DomesticCat as cat
where cat.name not in (
select name.nickName from Name as name
)
select cat.id, (select max(kit.weight) from cat.kitten kit)
from Cat as cat
HQL 副問い合わせは、 select または where 節だけで使われることに注意してください。
Note that subqueries can also utilize row value constructor syntax. See 「行値コンストラクタ構
文」 for more details.
14.14. HQL の 例
Hibernate queries can be quite powerful and complex. In fact, the power of the query language is one of
Hibernate's main selling points. Here are some example queries very similar to queries that I used on a
recent project. Note that most queries you will write are much simpler than these!
以下のクエリは特定の顧客と与えられた最小の合計値に対する未払い注文の注文 ID 、商品の数、注文の
合計を合計値で整列して返します。価格を決定する際、現在のカタログを使います。結果として返される
188
第14章 HQL: Hibernate クエリ言語
SQL クエリは ORDER、 ORDER_LINE、 PRODUCT 、 CAT ALOG および PRICE テーブルに対し4つの内部
結合と (関連しない) 副問い合わせを持ちます。
select order.id, sum(price.amount), count(item)
from Order as order
join order.lineItems as item
join item.product as product,
Catalog as catalog
join catalog.prices as price
where order.paid = false
and order.customer = :customer
and price.product = product
and catalog.effectiveDate < sysdate
and catalog.effectiveDate >= all (
select cat.effectiveDate
from Catalog as cat
where cat.effectiveDate < sysdate
)
group by order
having sum(price.amount) > :minAmount
order by sum(price.amount) desc
What a monster! Actually, in real life, I'm not very keen on subqueries, so my query was really more like
this:
select order.id, sum(price.amount), count(item)
from Order as order
join order.lineItems as item
join item.product as product,
Catalog as catalog
join catalog.prices as price
where order.paid = false
and order.customer = :customer
and price.product = product
and catalog = :currentCatalog
group by order
having sum(price.amount) > :minAmount
order by sum(price.amount) desc
次のクエリは各ステータスの支払い数を数えます。ただしすべての支払いが現在の利用者による最新のス
テータス変更である AWAIT ING_APPROVAL である場合を除きます。このクエリは2つの内部結合と
PAYMENT , PAYMENT _ST AT US および PAYMENT _ST AT US_CHANGE テーブルに対する関連副問い合わせ
を備えた SQL クエリに変換されます。
189
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
select count(payment), status.name
from Payment as payment
join payment.currentStatus as status
join payment.statusChanges as statusChange
where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
or (
statusChange.timeStamp = (
select max(change.timeStamp)
from PaymentStatusChange change
where change.payment = payment
)
and statusChange.user <> :currentUser
)
group by status.name, status.sortOrder
order by status.sortOrder
もし set の代わりに list として statusChanges コレクションをマッピングしたならば、はるかに簡単に
クエリを記述できるでしょう。
select count(payment), status.name
from Payment as payment
join payment.currentStatus as status
where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
group by status.name, status.sortOrder
order by status.sortOrder
次のクエリは現在のユーザーが所属する組織に対するアカウントおよび未払いの支払いをすべて返す MS
SQL Server の isNull() 関数を使用しています。このクエリは3つの内部結合と1つの外部結合、そして
ACCOUNT 、 PAYMENT 、 PAYMENT _ST AT US、 ACCOUNT _T YPE、 ORGANIZAT ION および ORG_USER
テーブルに対する副問い合わせ持った SQL に変換されます。
select account, payment
from Account as account
left outer join account.payments as payment
where :currentUser in elements(account.holder.users)
and PaymentStatus.UNPAID = isNull(payment.currentStatus.name,
PaymentStatus.UNPAID)
order by account.type.sortOrder, account.accountNumber, payment.dueDate
いくつかのデータベースについては、 (関連させられた) 副問い合わせの使用を避ける必要があるでしょ
う。
select account, payment
from Account as account
join account.holder.users as user
left outer join account.payments as payment
where :currentUser = user
and PaymentStatus.UNPAID = isNull(payment.currentStatus.name,
PaymentStatus.UNPAID)
order by account.type.sortOrder, account.accountNumber, payment.dueDate
14.15. 大 量 の UPDATE と DELETE
HQL now supports update, delete and insert ... select ... statements. See 「DML スタイル
190
第14章 HQL: Hibernate クエリ言語
の操作」 for details.
14.16. Tips & Tricks
実際に結果を返さなくてもクエリの結果数を数えることができます:
( (Integer) session.iterate("select count(*) from ....").next() ).intValue()
コレクションのサイズにより結果を並べ替えるためには以下のクエリを使用します:
select usr.id, usr.name
from User as usr
left join usr.messages as msg
group by usr.id, usr.name
order by count(msg)
使用しているデータベースがサブセレクトをサポートする場合、クエリの where 節でサイズによる選択条
件を設定できます:
from User usr where size(usr.messages) >= 1
If your database doesn't support subselects, use the following query:
select usr.id, usr.name
from User usr.name
join usr.messages msg
group by usr.id, usr.name
having count(msg) >= 1
As this solution can't return a User with zero messages because of the inner join, the following form is
also useful:
select usr.id, usr.name
from User as usr
left join usr.messages as msg
group by usr.id, usr.name
having count(msg) = 0
JavaBean のプロパティは、名前付きのクエリパラメータに結びつけることが出来ます:
Query q = s.createQuery("from foo Foo as foo where foo.name=:name and
foo.size=:size");
q.setProperties(fooBean); // fooBean has getName() and getSize()
List foos = q.list();
コレクションはフィルタ付き Query インターフェースを使用することでページをつけることができます:
Query q = s.createFilter( collection, "" ); // the trivial filter
q.setMaxResults(PAGE_SIZE);
q.setFirstResult(PAGE_SIZE * pageNumber);
List page = q.list();
コレクションの要素はクエリフィルタを使って、並べ替えやグループ分けが出来ます:
191
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
Collection orderedCollection = s.filter( collection, "order by this.amount" );
Collection counts = s.filter( collection, "select this.type, count(this) group by
this.type" );
コレクションを初期化せずにコレクションのサイズを得ることができます:
( (Integer) session.iterate("select count(*) from ....").next() ).intValue();
14.17. コ ン ポ ー ネ ン ト
HQL クエリでシンプルな値型を使用できるので、コンポーネントは、あらゆる点で使用できます。これは
select 節の中に現われます:
select p.name from from Person p
select p.name.first from from Person p
where the Person's name property is a component. Components can also be used in the where clause:
from from Person p where p.name = :name
from from Person p where p.name.first = :firstName
コンポーネントは order by 節でも使用可能です:
from from Person p order by p.name
from from Person p order by p.name.first
Another common use of components is in 「行値コンストラクタ構文」 row value constructors.
14.18. 行 値 コ ン ス ト ラ ク タ 構 文
下に位置するデータベースが ANSI SQL row value constructor 構文 (tuple 構文とよばれることも
あります) をサポートしていないとしても、 HQL はその使用をサポートしています。ここでは、一般的に
コンポーネントと連繋するマルチバリュー比較について触れます。ネームコンポーネントを定義する
Person エンティティを考えましょう:
from Person p where p.name.first='John' and p.name.last='Jingleheimer-Schmidt'
T hat's valid syntax, although a little verbose. It be nice to make this a bit more concise and use row
value constructor syntax:
from Person p where p.name=('John', 'Jingleheimer-Schmidt')
それを select 節で指定するのも効果的です。
select p.name from from Person p
次に row value constructor 構文の使用が有効なときは、サブクエリを使用して複数の値と比較する
192
第14章 HQL: Hibernate クエリ言語
必要があるときです:
from Cat as cat
where not ( cat.name, cat.color ) in (
select cat.name, cat.color from DomesticCat cat
)
この構文を使用するかどうか決定するときに考慮しなければならないことは、クエリがメタデータ内のコ
ンポーネントのサブプロパティの順番に依存していることです。
193
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 15章 Criteria クエリ
Hibernate には、直感的で拡張可能な criteria クエリ API が用意されています。
15.1. Criteria イ ン ス タ ン ス の 作 成
org.hibernate.Criteria インターフェースは特定の永続性クラスに対するクエリを表現します。
Session は Criteria インスタンスのファクトリです。
Criteria crit = sess.createCriteria(Cat.class);
crit.setMaxResults(50);
List cats = crit.list();
15.2. リ ザ ル ト セ ッ ト の 絞 込 み
org.hibernate.criterion.Criterion インターフェースのインスタンスは、個別のクエリクライ
テリオン(問い合わせの判定基準)を表します。 org.hibernate.criterion.Restrictions クラ
スは、ある組み込みの Criterion 型を取得するためのファクトリメソッドを持っています。
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.between("weight", minWeight, maxWeight) )
.list();
Restriction (限定)は、論理的にグループ化できます。
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.or(
Restrictions.eq( "age", new Integer(0) ),
Restrictions.isNull("age")
) )
.list();
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
.add( Restrictions.disjunction()
.add( Restrictions.isNull("age") )
.add( Restrictions.eq("age", new Integer(0) ) )
.add( Restrictions.eq("age", new Integer(1) ) )
.add( Restrictions.eq("age", new Integer(2) ) )
) )
.list();
元々ある Criterion 型(Restrictions のサブクラス) はかなりの範囲に及びますが、特に有用なのは
SQL を直接指定できるものです。
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)",
"Fritz%",
Hibernate.STRING) )
.list();
{alias} というプレースホルダは、問い合わせを受けたエンティティの行の別名によって置き換えられ
194
第15章 Criteria クエリ
ます。
criterion を得る別の手段は、 Property インスタンスから取得することです。 Property.forNam e()
を呼び出して、 Property インスタンスを作成できます。
Property age = Property.forName("age");
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.disjunction()
.add( age.isNull() )
.add( age.eq( new Integer(0) ) )
.add( age.eq( new Integer(1) ) )
.add( age.eq( new Integer(2) ) )
) )
.add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
.list();
15.3. 結 果 の 整 列
org.hibernate.criterion.Order を使って結果を並び替えることができます。
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50)
.list();
List cats = sess.createCriteria(Cat.class)
.add( Property.forName("name").like("F%") )
.addOrder( Property.forName("name").asc() )
.addOrder( Property.forName("age").desc() )
.setMaxResults(50)
.list();
15.4. 関 連
createCriteria() を使い、関連をナビゲートすることで、容易に関係するエンティティに制約を指定
できます。
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%") )
.createCriteria("kittens")
.add( Restrictions.like("name", "F%") )
.list();
2番目の createCriteria() は、 kittens コレクションの要素を参照する新しい Criteria インス
タンスを返すことに注意してください。
以下のような方法も、状況により有用です。
195
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
List cats = sess.createCriteria(Cat.class)
.createAlias("kittens", "kt")
.createAlias("mate", "mt")
.add( Restrictions.eqProperty("kt.name", "mt.name") )
.list();
(createAlias() は新しい Criteria インスタンスを作成しません。)
前の2つのクエリによって返される Cat インスタンスによって保持される kittens コレクションは、
criteria によって事前にフィルタリング されない ことに注意してください。もし criteria に適合する kitten
を取得したいなら、 ResultT ransform er を使わなければなりません。
List cats = sess.createCriteria(Cat.class)
.createCriteria("kittens", "kt")
.add( Restrictions.eq("name", "F%") )
.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
.list();
Iterator iter = cats.iterator();
while ( iter.hasNext() ) {
Map map = (Map) iter.next();
Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
Cat kitten = (Cat) map.get("kt");
}
15.5. 関 連 の 動 的 フ ェ ッ チ
setFetchMode() を使い、実行時に関連の復元方法を指定してもよいです。
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.setFetchMode("mate", FetchMode.EAGER)
.setFetchMode("kittens", FetchMode.EAGER)
.list();
T his query will fetch both m ate and kittens by outer join. See 「フェッチ戦略」 for more information.
15.6. ク エ リ の 例
org.hibernate.criterion.Exam ple クラスは、与えられたインスタンスからクエリクライテリオ
ンを構築できます。
Cat cat = new Cat();
cat.setSex('F');
cat.setColor(Color.BLACK);
List results = session.createCriteria(Cat.class)
.add( Example.create(cat) )
.list();
バージョンプロパティ、識別子、関連は無視されます。デフォルトでは null 値のプロパティは除外されま
す。
どのように Exam ple を適用するか調整することができます。
196
第15章 Criteria クエリ
Example example = Example.create(cat)
.excludeZeroes()
//exclude zero valued properties
.excludeProperty("color") //exclude the property named "color"
.ignoreCase()
//perform case insensitive string comparisons
.enableLike();
//use like for string comparisons
List results = session.createCriteria(Cat.class)
.add(example)
.list();
関連オブジェクトに criteria を指定するために、 example を使うことも可能です。
List results = session.createCriteria(Cat.class)
.add( Example.create(cat) )
.createCriteria("mate")
.add( Example.create( cat.getMate() ) )
.list();
15.7. 射 影 、 集 約 、 グ ル ー プ 化
org.hibernate.criterion.Projections クラスは Projection インスタンスのファクトリで
す。 setProjection() を呼び出すことで、クエリに射影を適用します。
List results = session.createCriteria(Cat.class)
.setProjection( Projections.rowCount() )
.add( Restrictions.eq("color", Color.BLACK) )
.list();
List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount() )
.add( Projections.avg("weight") )
.add( Projections.max("weight") )
.add( Projections.groupProperty("color") )
)
.list();
T here is no explicit "group by" necessary in a criteria query. Certain projection types are defined to be
grouping projections, which also appear in the SQL group by clause.
任意で射影に別名を付けられるため、射影される値は restriction や ordering 内から参照できます。別名を
つける2つの異なる方法を示します:
List results = session.createCriteria(Cat.class)
.setProjection( Projections.alias( Projections.groupProperty("color"), "colr" )
)
.addOrder( Order.asc("colr") )
.list();
List results = session.createCriteria(Cat.class)
.setProjection( Projections.groupProperty("color").as("colr") )
.addOrder( Order.asc("colr") )
.list();
alias() と as() メソッドは、 Projection インスタンスを別の名前の Projection インスタンスで
197
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
ラップするだけです。ショートカットとして、射影を射影リストに追加する際に、別名をつけられます:
List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount(), "catCountByColor" )
.add( Projections.avg("weight"), "avgWeight" )
.add( Projections.max("weight"), "maxWeight" )
.add( Projections.groupProperty("color"), "color" )
)
.addOrder( Order.desc("catCountByColor") )
.addOrder( Order.desc("avgWeight") )
.list();
List results = session.createCriteria(Domestic.class, "cat")
.createAlias("kittens", "kit")
.setProjection( Projections.projectionList()
.add( Projections.property("cat.name"), "catName" )
.add( Projections.property("kit.name"), "kitName" )
)
.addOrder( Order.asc("catName") )
.addOrder( Order.asc("kitName") )
.list();
射影の式に Property.forNam e() も使用できます:
List results = session.createCriteria(Cat.class)
.setProjection( Property.forName("name") )
.add( Property.forName("color").eq(Color.BLACK) )
.list();
List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount().as("catCountByColor") )
.add( Property.forName("weight").avg().as("avgWeight") )
.add( Property.forName("weight").max().as("maxWeight") )
.add( Property.forName("color").group().as("color" )
)
.addOrder( Order.desc("catCountByColor") )
.addOrder( Order.desc("avgWeight") )
.list();
15.8. ク エ リ お よ び サ ブ ク エ リ の 分 離
DetachedCriteria クラスにより、セッションスコープ外にクエリを作成できます。後で、任意の
Session を使って、実行できます。
DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
.add( Property.forName("sex").eq('F') );
Session session = ....;
Transaction txn = session.beginTransaction();
List results = query.getExecutableCriteria(session).setMaxResults(100).list();
txn.commit();
session.close();
198
第15章 Criteria クエリ
DetachedCriteria は、サブクエリを表現するためにも使えます。サブクエリを伴う Criterion インス
タンスは、 Subqueries もしくは Property から得られます。
DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
.setProjection( Property.forName("weight").avg() );
session.createCriteria(Cat.class)
.add( Property.forName("weight).gt(avgWeight) )
.list();
DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
.setProjection( Property.forName("weight") );
session.createCriteria(Cat.class)
.add( Subqueries.geAll("weight", weights) )
.list();
相互関係があるサブクエリでさえも可能です:
DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
.setProjection( Property.forName("weight").avg() )
.add( Property.forName("cat2.sex").eqProperty("cat.sex") );
session.createCriteria(Cat.class, "cat")
.add( Property.forName("weight).gt(avgWeightForSex) )
.list();
15.9. 自 然 識 別 子 に よ る ク エ リ
criteria クエリを含むたいていのクエリにとって、クエリキャッシュはあまり効率がよくないです。なぜな
ら、クエリキャッシュが頻繁に無効になるためです。しかしながら、キャッシュを無効にするアルゴリズ
ムを最適化できる特別なクエリの種類が1つあります。更新されない自然キーによる検索です。いくつか
のアプリケーションでは、この種類のクエリが頻繁に現れます。このような使われ方のために、 criteria
API は特別な対策を提供します。
First, you should map the natural key of your entity using <natural-id>, and enable use of the
second-level cache.
<class name="User">
<cache usage="read-write"/>
<id name="id">
<generator class="increment"/>
</id>
<natural-id>
<property name="name"/>
<property name="org"/>
</natural-id>
<property name="password"/>
</class>
注記: 変更される 自然キーを持つエンティティにこの機能を使うのは、意図されていない使い方です。
次に、 Hibernate クエリキャッシュを有効にします。
これで、 Restrictions.naturalId() により、より効率的なキャッシュアルゴリズムを使用できま
す。
199
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
session.createCriteria(User.class)
.add( Restrictions.naturalId()
.set("name", "gavin")
.set("org", "hb")
).setCacheable(true)
.uniqueResult();
200
第16章 ネイティブ SQL
第 16章 ネイティブ SQL
データベースのネイティブ SQL 方言を使ってクエリを表現することもできます。クエリヒントや Oracle
の CONNECT キーワードのように、データベース独自の機能を利用したいときに使えます。 SQL/JDBC
を直接使用しているアプリケーションから Hibernate への移行も容易にしています。
Hibernate3 では、生成、更新、削除、読み込み処理のようなすべての SQL (ストアドプロシージャを含
む)を手書きできます。
16.1. SQLQuery の 使 用
ネイティブな SQL クエリの実行は SQLQuery インターフェースを通して制御します。 SQLQuery イン
ターフェースは Session.createSQLQuery() を呼び出して取得します。この API を使って問い合わせ
する方法を以下で説明します。
16.1.1. スカラーのクエリ
最も基本的な SQL クエリはスカラー(値)のリストを得ることです。
sess.createSQLQuery("SELECT * FROM CATS").list();
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
これらはどちらも、 CAT S テーブルの各カラムのスカラー値を含む Object 配列(Object[])のリストを返
します。返すスカラー値の実際の順番と型を推定するために、 Hibernate は ResultSetMetadata を使用し
ます。
ResultSetMetadata を使用するオーバーヘッドを避けるため、もしくは単に何が返されるか明確にす
るため、 addScalar() を使えます。
sess.createSQLQuery("SELECT * FROM CATS")
.addScalar("ID", Hibernate.LONG)
.addScalar("NAME", Hibernate.STRING)
.addScalar("BIRTHDATE", Hibernate.DATE)
このクエリで指定されているものを下記に示します:
SQL クエリ文字列
返されるカラムと型
これはまだ Object 配列を返しますが、 ResultSetMetdata を使用しません。ただし、その代わりに基
礎にあるリザルトセットから ID、NAME、BIRT HDAT E カラムをそれぞれ Long、String、Short として明
示的に取得します。これは3つのカラムを返すのみであることも意味します。たとえ、クエリが * を使用
し、列挙した3つより多くのカラムを返せるとしてもです。
スカラーの型情報を省くこともできます。
sess.createSQLQuery("SELECT * FROM CATS")
.addScalar("ID", Hibernate.LONG)
.addScalar("NAME")
.addScalar("BIRTHDATE")
これは本質的に前と同じクエリですが、 NAME と BIRT HDAT E の型を決めるために
ResultSetMetaData を使用します。一方、 ID の型は明示的に指定されています。
ResultSetMetaData から返される java.sql.T ypes を Hibernate の型に マッピングすることは、 Dialect が
201
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
制御します。明示された型がマッピングされていないか、結果の型が期待したものと異なる場合、 Dialect
の registerHibernateT ype を呼び出し、カスタマイズできます。
16.1.2. エンティティのクエリ
T he above queries were all about returning scalar values, basically returning the "raw" values from the
resultset. T he following shows how to get entity objects from a native sql query via addEntity().
sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
このクエリで指定されているものを下記に示します:
SQL クエリ文字列
クエリが返すエンティティと SQL テーブルの別名
Cat が ID 、 NAME 、 BIRT HDAT E のカラムを使ってクラスにマッピングされる場合、上記のクエリはど
ちらも、要素が Cat エンティティであるリストを返します。
If the entity is mapped with a m any-to-one to another entity it is required to also return this when
performing the native query, otherwise a database specific "column not found" error will occur. T he
additional columns will automatically be returned when using the * notation, but we prefer to be explicit as
in the following example for a m any-to-one to a Dog:
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM
CATS").addEntity(Cat.class);
これにより cat.getDog() が正しく機能します。
16.1.3. 関連とコレクションの操作
プロキシを初期化するための余分な処理を避けるため、 Dog の中で即時結合できます。これは
addJoin() メソッドにより行います。関連もしくはコレクションに結合できます。
sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS
c, DOGS d
WHERE c.DOG_ID = d.D_ID")
.addEntity("cat", Cat.class)
.addJoin("cat.dog");
In this example the returned Cat's will have their dog property fully initialized without any extra roundtrip
to the database. Notice that we added a alias name ("cat") to be able to specify the target property path
of the join. It is possible to do the same eager joining for collections, e.g. if the Cat had a one-to-many to
Dog instead.
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c,
DOGS d
WHERE c.ID = d.CAT_ID")
.addEntity("cat", Cat.class)
.addJoin("cat.dogs");
現在のところ、 Hibernate で使いやすくするための SQL クエリの拡張なしに、ネイティブクエリで何か
を可能にする限界に来ています。同じ型のエンティティを複数返す際や、デフォルトの別名や列名で十分
ではない場合に、問題は起こり始めます。
202
第16章 ネイティブ SQL
16.1.4. 複数エンティティの取得
ここまでは、リザルトセットのカラム名は、マッピングドキュメントで指定されたカラム名と同じである
と仮定していました。複数のテーブルが同じカラム名を持つ場合があるため、複数テーブルを結合する
SQL クエリで問題となる場合があります。
下記のような(失敗しそうな)クエリでは、カラム別名インジェクション(column alias injection)が必
要です:
sess.createSQLQuery("SELECT c.*, m.*
c.ID")
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class)
FROM CATS c, CATS m WHERE c.MOTHER_ID =
T he intention for this query is to return two Cat instances per row, a cat and its mother. T his will fail
since there is a conflict of names since they are mapped to the same column names and on some
databases the returned column aliases will most likely be on the form "c.ID", "c.NAME", etc. which are not
equal to the columns specificed in the mappings ("ID" and "NAME").
下記の形式は、カラム名が重複しても大丈夫です:
sess.createSQLQuery("SELECT {cat.*}, {mother.*}
c.MOTHER_ID = c.ID")
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class)
FROM CATS c, CATS m WHERE
このクエリで指定されているものを下記に示します:
SQL クエリ文字列 (Hibernate がカラムの別名を挿入するためのプレースホルダを含む)
クエリによって返されるエンティティ
T he {cat.*} and {mother.*} notation used above is a shorthand for "all properties". Alternatively, you may
list the columns explicity, but even in this case we let Hibernate inject the SQL column aliases for each
property. T he placeholder for a column alias is just the property name qualified by the table alias. In the
following example, we retrieve Cats and their mothers from a different table (cat_log) to the one declared
in the mapping metadata. Notice that we may even use the property aliases in the where clause if we
like.
String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
"BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
"FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class).list()
16.1.4 .1. 別名とプロパティのリファレンス
多くの場合、上記のような別名インジェクションが必要です。ただし、複合プロパティ、継承識別子、コ
レクションなどのようなより複雑なマッピングと関連するクエリがなければです。ある特定の別名を使用
することにより、 Hibernate は適切な別名を挿入できます。
別名インジェクションとして使用できるものを下表に示します。注記:下表の別名は一例です。それぞれ
の別名は一意であり、使用する際にはおそらく異なる名前を持ちます。
203
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
表 16.1 別名に挿入する名前
説明
構文
例
単純なプロパティ
{[aliasnam e].[propertyn
am e]
A_NAME as {item .nam e}
複合プロパティ
{[aliasnam e].[com ponent
nam e].[propertynam e]}
CURRENCY as
{item .am ount.currency},
VALUE as
{item .am ount.value}
エンティティのクラスを識別す
る値
{[aliasnam e].class}
DISC as {item .class}
エンティティの全プロパティ
{[aliasnam e].* }
{item .* }
コレクションのキー
{[aliasnam e].key}
ORGID as {coll.key}
コレクションの ID
{[aliasnam e].id}
EMPID as {coll.id}
コレクションの要素
{[aliasnam e].elem ent}
XID as {coll.elem ent}
コレクションの要素のプロパ
ティ
{[aliasnam e].elem ent.[p
ropertynam e]}
NAME as
{coll.elem ent.nam e}
コレクションの要素の全プロパ
ティ
{[aliasnam e].elem ent.* }
{coll.elem ent.* }
コレクションの全プロパティ
{[aliasnam e].* }
{coll.* }
16.1.5. 管理されていないエンティティの取得
ネイティブ SQL クエリに ResultT ransformer を適用できます。下記のように、例えば、管理されていな
いエンティティを返します。
sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
.setResultTransformer(Transformers.aliasToBean(CatDTO.class))
このクエリで指定されているものを下記に示します:
SQL クエリ文字列
結果を変換したもの
上記のクエリは、インスタンス化し、 NAME と BIRT HDAT E の値を対応するプロパティもしくはフィー
ルドに挿入した CatDT O のリストを返します。
16.1.6. 継承の制御
継承の一部としてマッピングされたエンティティを問い合わせるネイティブ SQL クエリは、ベースのク
ラスとそのすべてのサブクラスのプロパティすべてを含まなければなりません。
16.1.7. パラメータ
ネイティブ SQL クエリは、以下のように、名前付きパラメータ(:name)と同様に位置パラメータをサ
ポートします:
204
第16章 ネイティブ SQL
Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like
?").addEntity(Cat.class);
List pusList = query.setString(0, "Pus%").list();
query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like
:name").addEntity(Cat.class);
List pusList = query.setString("name", "Pus%").list();
16.2. 名 前 付 き SQL ク エ リ
名前付き SQL クエリはマッピングドキュメントで定義することができ、名前付き HQL クエリと全く同じ
方法で呼ぶことができます。この場合、 addEntity() を呼び出す必要は ありません 。
<sql-query name="persons">
<return alias="person" class="eg.Person"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
</sql-query>
List people = sess.getNamedQuery("persons")
.setString("namePattern", namePattern)
.setMaxResults(50)
.list();
T he <return-join> and <load-collection> elements are used to join associations and define
queries which initialize collections, respectively.
<sql-query name="personsWith">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
adddress.STREET AS {address.street},
adddress.CITY AS {address.city},
adddress.STATE AS {address.state},
adddress.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
</sql-query>
A named SQL query may return a scalar value. You must declare the column alias and Hibernate type
using the <return-scalar> element:
205
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<sql-query name="mySqlQuery">
<return-scalar column="name" type="string"/>
<return-scalar column="age" type="long"/>
SELECT p.NAME AS name,
p.AGE AS age,
FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
</sql-query>
You can externalize the resultset mapping informations in a <resultset> element to either reuse them
accross several named queries or through the setResultSetMapping() API.
<resultset name="personAddress">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>
</resultset>
<sql-query name="personsWith" resultset-ref="personAddress">
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
adddress.STREET AS {address.street},
adddress.CITY AS {address.city},
adddress.STATE AS {address.state},
adddress.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
</sql-query>
代わりに、 hbm ファイル内のリザルトセットのマッピング情報を直接 Java コードの中で使用できます。
List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother
= cat.id"
)
.setResultSetMapping("catAndKitten")
.list();
16.2.1. 列と列の別名を明示的に指定するために return-property を使う
With <return-property> you can explicitly tell Hibernate what column aliases to use, instead of
using the {}-syntax to let Hibernate inject its own aliases.
<sql-query name="mySqlQuery">
<return alias="person" class="eg.Person">
<return-property name="name" column="myName"/>
<return-property name="age" column="myAge"/>
<return-property name="sex" column="mySex"/>
</return>
SELECT person.NAME AS myName,
person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name
</sql-query>
<return-property> also works with multiple columns. T his solves a limitation with the {}-syntax
206
第16章 ネイティブ SQL
which can not allow fine grained control of multi-column properties.
<sql-query name="organizationCurrentEmployments">
<return alias="emp" class="Employment">
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
<return-property name="endDate" column="myEndDate"/>
</return>
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
FROM EMPLOYMENT
WHERE EMPLOYER = :id AND ENDDATE IS NULL
ORDER BY STARTDATE ASC
</sql-query>
Notice that in this example we used <return-property> in combination with the {}-syntax for
injection. Allowing users to choose how they want to refer column and properties.
If your mapping has a discriminator you must use <return-discrim inator> to specify the
discriminator column.
16.2.2. 問い合わせするためにストアドプロシージャを使う
Hibernate はバージョン3から、ストアドプロシージャとストアド関数経由の問い合わせがサポートされま
した。以降の文書の多くは、両方に当てはまります。ストアドプロシージャやストアド関数を Hibernate
で使うためには、1番目の出力パラメータとしてリザルトセットを返さなければなりません。 Oracle
9(もしくはそれ以上のバージョン)のストアドプロシージャの例を以下に示します:
CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR
AS
st_cursor SYS_REFCURSOR;
BEGIN
OPEN st_cursor FOR
SELECT EMPLOYEE, EMPLOYER,
STARTDATE, ENDDATE,
REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT;
RETURN st_cursor;
END;
Hibernate でこのクエリを使うためには、名前付きクエリでマッピングする必要があります。
207
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<sql-query name="selectAllEmployees_SP" callable="true">
<return alias="emp" class="Employment">
<return-property name="employee" column="EMPLOYEE"/>
<return-property name="employer" column="EMPLOYER"/>
<return-property name="startDate" column="STARTDATE"/>
<return-property name="endDate" column="ENDDATE"/>
<return-property name="regionCode" column="REGIONCODE"/>
<return-property name="id" column="EID"/>
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
</return>
{ ? = call selectAllEmployments() }
</sql-query>
Notice stored procedures currently only return scalars and entities. <return-join> and <loadcollection> are not supported.
16.2.2.1. ストアドプロシージャを使う上でのルールと制限
Hibernate でストアドプロシージャや関数を使うためには、そのプロシージャはいくつかのルールに準拠
する必要があります。ルールに準拠していないプロシージャは、 Hibernate で使うことはできません。そ
れでも、準拠していないプロシージャを使いたいのであれば、 session.connection() を通じて実行
しなければなりません。ルールはデータベースごとに異なります。ストアドプロシージャのセマンティッ
クスとシンタックスは、データベースベンダごとに異なるためです。
Stored procedure queries can't be paged with setFirstResult()/setMaxResults().
Recommended call form is standard SQL92: { ? = call functionNam e(<param eters>) } or {
? = call procedureNam e(<param eters>}. Native call syntax is not supported.
Oracle には下記のルールが適用されます:
関数はリザルトセットを返さなければなりません。プロシージャの第一引数はリザルトセットを返す
ため、 OUT でなければなりません。 Oracle 9 と 10 では、 SYS_REFCURSOR を使うことによってで
きます。 Oracle では REF CURSOR 型を定義する必要があります。 Oracle の文献を参照してくださ
い。
Sybase と MS SQL サーバーに適用されるルールを下記に示します:
プロシージャはリザルトセットを返さなければなりません。サーバーは複数のリザルトセットと更新
カウントを返しますが、 Hibernate は1つ目のリザルトセットだけを返すことに注意してください。そ
の他はすべて捨てられます。
プロシージャの中で SET NOCOUNT ON を有効にできれば、おそらく効率がよくなるでしょう。しか
し、これは必要条件ではありません。
16.3. 作 成 、 更 新 、 削 除 の た め の カ ス タ ム SQL
Hibernate3 can use custom SQL statements for create, update, and delete operations. T he class and
collection persisters in Hibernate already contain a set of configuration time generated strings (insertsql,
deletesql, updatesql etc.). T he mapping tags <sql-insert>, <sql-delete>, and <sql-update>
override these strings:
208
第16章 ネイティブ SQL
<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>
SQL を直接データベースで実行するため、好みの方言を自由に使用できます。データベース独自の SQL
を使えば、当然マッピングのポータビリティが下がります。
callable 属性をセットすれば、ストアドプロシージャを使用できます:
<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
<sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
<sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
</class>
今のところ、位置パラメータの順番はとても重要です。すなわち、 Hibernate が期待する順序でなければ
なりません。
org.hiberante.persister.entity レベルのデバッグログを有効にすることによって、期待される
順番を確かめられます。このレベルを有効にすることにより、エンティティの作成、更新、削除などで使
用される静的な SQL が出力されます。(期待される順序を確認するためには、 Hibernate が生成する静
的な SQL をオーバーライドするカスタム SQL をマッピングファイルに含めないことを忘れないでくださ
い。)
ストアドプロシージャは挿入/更新/削除された行数を返す必要があります(読み込みの場合は、返さない
よりは返す方がよいです)。実行時に Hibernate が SQL 文の成功をチェックするからです。 Hibernate
は、 CUD 処理のための数値の出力パラメータとして、 SQL 文の最初のパラメータをいつも記録します:
CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
RETURN NUMBER IS
BEGIN
update PERSON
set
NAME = uname,
where
ID = uid;
return SQL%ROWCOUNT;
END updatePerson;
16.4. ロ ー ド の た め の カ ス タ ム SQL
エンティティを読み込むための独自の SQL (もしくは HQL)クエリも宣言できます:
209
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<sql-query name="person">
<return alias="pers" class="Person" lock-mode="upgrade"/>
SELECT NAME AS {pers.name}, ID AS {pers.id}
FROM PERSON
WHERE ID=?
FOR UPDATE
</sql-query>
これは、まさに(以前議論した)名前付きクエリの宣言です。この名前付きクエリをクラスのマッピング
から参照できます:
<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<loader query-ref="person"/>
</class>
これはストアドプロシージャでさえも動作します。
次のように、コレクションをロードするためのクエリさえ定義してよいです:
<set name="employments" inverse="true">
<key/>
<one-to-many class="Employment"/>
<loader query-ref="employments"/>
</set>
<sql-query name="employments">
<load-collection alias="emp" role="Person.employments"/>
SELECT {emp.*}
FROM EMPLOYMENT emp
WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC
</sql-query>
次のように、結合フェッチによりコレクションをロードするエンティティローダーを定義できます:
<sql-query name="person">
<return alias="pers" class="Person"/>
<return-join alias="emp" property="pers.employments"/>
SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID
WHERE ID=?
</sql-query>
210
第17章 データのフィルタリング
第 17章 データのフィルタリング
Hibernate3 provides an innovative new approach to handling data with "visibility" rules. A Hibernate filter
is a global, named, parameterized filter that may be enabled or disabled for a particular Hibernate
session.
17.1. Hibernate の フ ィ ル タ
Hibernate3 adds the ability to pre-define filter criteria and attach those filters at both a class and a
collection level. A filter criteria is the ability to define a restriction clause very similiar to the existing
"where" attribute available on the class and various collection elements. Except these filter conditions
can be parameterized. T he application can then make the decision at runtime whether given filters
should be enabled and what their parameter values should be. Filters can be used like database views,
but parameterized inside the application.
In order to use filters, they must first be defined and then attached to the appropriate mapping elements.
T o define a filter, use the <filter-def/> element within a <hibernate-m apping/> element:
<filter-def name="myFilter">
<filter-param name="myFilterParam" type="string"/>
</filter-def>
そうしてフィルタはクラスへと結び付けられます:
<class name="myClass" ...>
...
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</class>
また、コレクションに対しては次のようになります:
<set ...>
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</set>
どちらに対しても (また、それぞれを複数) 同時に設定することもできます。
Session 上のメソッドは enableFilter(String filterNam e)、 getEnabledFilter(String
filterNam e)、 disableFilter(String filterNam e) です。デフォルトでは、フィルタは与え
られたセッションに対して使用 できません 。 Filter インスタンスを返り値とする
Session.enabledFilter() メソッドを使うことで、フィルタは明示的に使用可能となります。上で
定義した単純なフィルタの使用は、このようになります:
session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");
org.hibernate.Filter インターフェースのメソッドは、 Hibernate の多くに共通しているメソッド連鎖を許
していることに注意してください。
有効なレコードデータパターンを持つ一時データを使った完全な例です:
211
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<filter-def name="effectiveDate">
<filter-param name="asOfDate" type="date"/>
</filter-def>
<class name="Employee" ...>
...
<many-to-one name="department" column="dept_id" class="Department"/>
<property name="effectiveStartDate" type="date" column="eff_start_dt"/>
<property name="effectiveEndDate" type="date" column="eff_end_dt"/>
...
<!-Note that this assumes non-terminal records have an eff_end_dt set to
a max db date for simplicity-sake
-->
<filter name="effectiveDate"
condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</class>
<class name="Department" ...>
...
<set name="employees" lazy="true">
<key column="dept_id"/>
<one-to-many class="Employee"/>
<filter name="effectiveDate"
condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</set>
</class>
常に現在の有効レコードを返却することを保証するために、単純に、社員データの検索より前にセッショ
ン上のフィルタを有効にします:
Session session = ...;
session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
List results = session.createQuery("from Employee as e where e.salary >
:targetSalary")
.setLong("targetSalary", new Long(1000000))
.list();
上記の HQL では、結果の給料の制約について明示的に触れただけですが、有効になっているフィルタの
おかげで、このクエリは給料が100万ドル以上の現役の社員だけを返します。
注記: (HQL かロードフェッチで)外部結合を持つフィルタを使うつもりなら、条件式の方向に注意して
ください。これは左外部結合のために設定するのが最も安全です。一般的に、演算子の後カラム名に続け
て最初のパラメータを配置してください。
After being defined a filter might be attached to multiple entities and/or collections each with its own
condition. T hat can be tedious when the conditions are the same each time. T hus <filter-def/>
allows defining a default condition, either as an attribute or CDAT A:
<filter-def name="myFilter" condition="abc > xyz">...</filter-def>
<filter-def name="myOtherFilter">abc=xyz</filter-def>
このデフォルトのコンディションは、コンディションを指定せずに何かにアタッチされる場合いつでも使
われます。これは、特定のケースにおいてデフォルトのコンディションをオーバーライドするフィルター
のアタッチメントの一部として、特定のコンディションを与えることができることを意味します。
212
第18章 XML マッピング
第 18章 XML マッピング
Note that this is an experimental feature in Hibernate 3.0 and is under extremely active development.
18.1. XML デ ー タ で の 作 業
Hibernate では永続性の POJO を使って作業するのとほぼ同じようなやり方で、永続性の XML データを
使って作業できます。解析された XML ツリーは POJO の代わりにオブジェクトレベルで関係データを表
わす別の方法であるとみなされています。
Hibernate supports dom4j as API for manipulating XML trees. You can write queries that retrieve dom4j
trees from the database and have any modification you make to the tree automatically synchronized to
the database. You can even take an XML document, parse it using dom4j, and write it to the database
with any of Hibernate's basic operations: persist(), saveOrUpdate(), m erge(), delete(),
replicate() (merging is not yet supported).
データのインポート/エクスポート、 JMS によるエンティティデータの外部化や SOAP 、 XSLT ベースの
レポートなど、この機能には多くの用途があります。
単一のマッピングは、クラスのプロパティと XML ドキュメントのノードを同時にデータベースへマッピ
ングするために使うことができます。またマッピングするクラスがなければ、 XML だけをマッピングす
るために使うことができます。
18.1.1. XML とクラスのマッピングを同時に指定する
これは POJO と XML を同時にマッピングする例です:
<class name="Account"
table="ACCOUNTS"
node="account">
<id name="accountId"
column="ACCOUNT_ID"
node="@id"/>
<many-to-one name="customer"
column="CUSTOMER_ID"
node="customer/@id"
embed-xml="false"/>
<property name="balance"
column="BALANCE"
node="balance"/>
...
</class>
18.1.2. XML マッピングだけを指定する
これは POJO クラスがないマッピングの例です:
213
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class entity-name="Account"
table="ACCOUNTS"
node="account">
<id name="id"
column="ACCOUNT_ID"
node="@id"
type="string"/>
<many-to-one name="customerId"
column="CUSTOMER_ID"
node="customer/@id"
embed-xml="false"
entity-name="Customer"/>
<property name="balance"
column="BALANCE"
node="balance"
type="big_decimal"/>
...
</class>
このマッピングにより、 dom4j ツリーか、プロパティ名/値の組のグラフ(java の Map)としてデータに
アクセスできます。プロパティの名前は、 HQL クエリ内で参照できる純粋な論理構造です。
18.2. XML マ ッ ピ ン グ の メ タ デ ー タ
Many Hibernate mapping elements accept the node attribute. T his let's you specify the name of an XML
attribute or element that holds the property or entity data. T he format of the node attribute must be one
of the following:
"elem ent-nam e" - map to the named XML element
"@ attribute-nam e" - map to the named XML attribute
"." - map to the parent element
"elem ent-nam e/@ attribute-nam e" - map to the named attribute of the named element
For collections and single valued associations, there is an additional em bed-xm l attribute. If em bedxm l="true", the default, the XML tree for the associated entity (or collection of value type) will be
embedded directly in the XML tree for the entity that owns the association. Otherwise, if em bedxm l="false", then only the referenced identifier value will appear in the XML for single point
associations and collections will simply not appear at all.
You should be careful not to leave em bed-xm l="true" for too many associations, since XML does not
deal well with circularity!
214
第18章 XML マッピング
<class name="Customer"
table="CUSTOMER"
node="customer">
<id name="id"
column="CUST_ID"
node="@id"/>
<map name="accounts"
node="."
embed-xml="true">
<key column="CUSTOMER_ID"
not-null="true"/>
<map-key column="SHORT_DESC"
node="@short-desc"
type="string"/>
<one-to-many entity-name="Account"
embed-xml="false"
node="account"/>
</map>
<component name="name"
node="name">
<property name="firstName"
node="first-name"/>
<property name="initial"
node="initial"/>
<property name="lastName"
node="last-name"/>
</component>
...
</class>
この例では、実際の account のデータではなく、 account の id のコレクションを埋め込むことにしまし
た。続きの HQL クエリです:
from Customer c left join fetch c.accounts where c.lastName like :lastName
このようなデータセットを返すでしょう:
<customer id="123456789">
<account short-desc="Savings">987632567</account>
<account short-desc="Credit Card">985612323</account>
<name>
<first-name>Gavin</first-name>
<initial>A</initial>
<last-name>King</last-name>
</name>
...
</customer>
If you set em bed-xm l="true" on the <one-to-m any> mapping, the data might look more like this:
215
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<customer id="123456789">
<account id="987632567" short-desc="Savings">
<customer id="123456789"/>
<balance>100.29</balance>
</account>
<account id="985612323" short-desc="Credit Card">
<customer id="123456789"/>
<balance>-2370.34</balance>
</account>
<name>
<first-name>Gavin</first-name>
<initial>A</initial>
<last-name>King</last-name>
</name>
...
</customer>
18.3. XML デ ー タ を 扱 う
Let's rearead and update XML documents in the application. We do this by obtaining a dom4j session:
Document doc = ....;
Session session = factory.openSession();
Session dom4jSession = session.getSession(EntityMode.DOM4J);
Transaction tx = session.beginTransaction();
List results = dom4jSession
.createQuery("from Customer c left join fetch c.accounts where c.lastName like
:lastName")
.list();
for ( int i=0; i<results.size(); i++ ) {
//add the customer data to the XML document
Element customer = (Element) results.get(i);
doc.add(customer);
}
tx.commit();
session.close();
Session session = factory.openSession();
Session dom4jSession = session.getSession(EntityMode.DOM4J);
Transaction tx = session.beginTransaction();
Element cust = (Element) dom4jSession.get("Customer", customerId);
for ( int i=0; i<results.size(); i++ ) {
Element customer = (Element) results.get(i);
//change the customer name in the XML and database
Element name = customer.element("name");
name.element("first-name").setText(firstName);
name.element("initial").setText(initial);
name.element("last-name").setText(lastName);
}
tx.commit();
session.close();
216
第18章 XML マッピング
It is extremely useful to combine this feature with Hibernate's replicate() operation to implement
XML-based data import/export.
217
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 19章 パフォーマンスの改善
19.1. フ ェ ッ チ 戦 略
フェッチ戦略 は、アプリケーションが関連をナビゲートする必要があるときに、 Hibernate が関連オブ
ジェクトを復元するために使用する戦略です。フェッチ戦略は O/R マッピングのメタデータに宣言する
か、特定の HQL 、 Criteria クエリでオーバーライドします。
Hibernate3 は次に示すフェッチ戦略を定義しています:
結合フェッチ - Hibernate は OUT ER JOIN を使って、関連するインスタンスやコレクションを1つの
SELECT で復元します。
Select fetching - a second SELECT is used to retrieve the associated entity or collection. Unless you
explicitly disable lazy fetching by specifying lazy="false", this second select will only be executed
when you actually access the association.
Subselect fetching - a second SELECT is used to retrieve the associated collections for all entities
retrieved in a previous query or fetch. Unless you explicitly disable lazy fetching by specifying
lazy="false", this second select will only be executed when you actually access the association.
バッチフェッチ - セレクトフェッチのための最適化された戦略 - Hibernate はエンティティのインスタ
ンスやコレクションの一群を1回の SELECT で復元します。これは主キーや外部キーのリストを指定
することにより行います。
Hibernate は次に示す戦略とも区別をします:
即時フェッチ - 所有者のオブジェクトがロードされたときに、関連、コレクションは即時にフェッチ
されます。
遅延コレクションフェッチ - アプリケーションがコレクションに対して操作を行ったときにコレク
ションをフェッチします。(これはコレクションに対するデフォルトの動作です)
"Extra-lazy" collection fetching - individual elements of the collection are accessed from the database
as needed. Hibernate tries not to fetch the whole collection into memory unless absolutely needed
(suitable for very large collections)
プロキシフェッチ - 単一値関連は、識別子の getter 以外のメソッドが関連オブジェクトで呼び出され
るときにフェッチされます。
"No-proxy" fetching - a single-valued association is fetched when the instance variable is accessed.
Compared to proxy fetching, this approach is less lazy (the association is fetched even when only
the identifier is accessed) but more transparent, since no proxy is visible to the application. T his
approach requires buildtime bytecode instrumentation and is rarely necessary.
遅延属性フェッチ - 属性や単一値関連は、インスタンス変数にアクセスしたときにフェッチされま
す。この方法はビルド時のバイトコード組み込みが必要になり、使う場面はまれです。
We have two orthogonal notions here: when is the association fetched, and how is it fetched (what SQL
is used). Don't confuse them! We use fetch to tune performance. We may use lazy to define a
contract for what data is always available in any detached instance of a particular class.
19.1.1. 遅延関連の働き
デフォルトでは、 Hibernate3 はコレクションに対しては遅延セレクトフェッチを使い、単一値関連には
遅延プロキシフェッチを使います。これらのデフォルト動作はほぼすべてのアプリケーションのほぼすべ
ての関連で意味があります。
注:hibernate.default_batch_fetch_size をセットしたときは、 Hibernate は遅延フェッチのた
めのバッチフェッチ最適化を使うでしょう(この最適化はより細かいレベルで有効にすることも出来ま
218
第19章 パフォーマンスの改善
す)。
しかし、遅延フェッチは知っておかなければならない一つの問題があります。 Hibernate の session を
オープンしているコンテキストの外から遅延関連にアクセスすると、例外が発生します。例:
s = sessions.openSession();
Transaction tx = s.beginTransaction();
User u = (User) s.createQuery("from User u where u.name=:userName")
.setString("userName", userName).uniqueResult();
Map permissions = u.getPermissions();
tx.commit();
s.close();
Integer accessLevel = (Integer) permissions.get("accounts");
// Error!
Session がクローズされたとき、 permissions コレクションは初期化されていないため、このコレク
ションは自身の状態をロードできません。 Hibernate は切り離されたオブジェクトの遅延初期化はサポー
トしていません 。修正方法として、コレクションから読み込みを行うコードをトランザクションをコミッ
トする直前に移動させます。
Alternatively, we could use a non-lazy collection or association, by specifying lazy="false" for the
association mapping. However, it is intended that lazy initialization be used for almost all collections and
associations. If you define too many non-lazy associations in your object model, Hibernate will end up
needing to fetch the entire database into memory in every transaction!
On the other hand, we often want to choose join fetching (which is non-lazy by nature) instead of select
fetching in a particular transaction. We'll now see how to customize the fetching strategy. In Hibernate3,
the mechanisms for choosing a fetch strategy are identical for single-valued associations and
collections.
19.1.2. フェッチ戦略のチューニング
セレクトフェッチ(デフォルト)は N+1 セレクト問題という大きな弱点があるため、マッピング定義で
結合フェッチを有効にすることができます:
<set name="permissions"
fetch="join">
<key column="userId"/>
<one-to-many class="Permission"/>
</set
<many-to-one name="mother" class="Cat" fetch="join"/>
マッピング定義で定義した フェッチ 戦略は次のものに影響します:
get() や load() による復元
関連にナビゲートしたときに発生する暗黙的な復元
Criteria クエリ
サブセレクト フェッチを使う HQL クエリ
たとえどんなフェッチ戦略を使ったとしても、遅延ではないグラフはメモリに読み込まれることが保証さ
れます。つまり、特定の HQL クエリを実行するためにいくつかの SELECT 文が即時実行されることがあ
るので注意してください。
219
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
Usually, we don't use the mapping document to customize fetching. Instead, we keep the default
behavior, and override it for a particular transaction, using left join fetch in HQL. T his tells
Hibernate to fetch the association eagerly in the first select, using an outer join. In the Criteria query
API, you would use setFetchMode(FetchMode.JOIN).
もし get() や load() で使われるフェッチ戦略を変えたいと感じたときには、単純に Criteria クエ
リを使ってください。例:
User user = (User) session.createCriteria(User.class)
.setFetchMode("permissions", FetchMode.JOIN)
.add( Restrictions.idEq(userId) )
.uniqueResult();
(T his is Hibernate's equivalent of what some ORM solutions call a "fetch plan".)
N+1 セレクト問題を避けるためのまったく違う方法は、第2レベルキャッシュを使うことです。
19.1.3. 単一端関連プロキシ
Lazy fetching for collections is implemented using Hibernate's own implementation of persistent
collections. However, a different mechanism is needed for lazy behavior in single-ended associations.
T he target entity of the association must be proxied. Hibernate implements lazy initializing proxies for
persistent objects using runtime bytecode enhancement (via the excellent CGLIB library).
デフォルトでは、 Hibernate3 は(開始時に)すべての永続クラスのプロキシを生成し、それらを使っ
て、 m any-to-one や one-to-one 関連の遅延フェッチを可能にしています。
マッピングファイルで proxy 属性によって、クラスのプロキシインターフェースとして使うインター
フェースを宣言できます。デフォルトでは、 Hibernate はそのクラスのサブクラスを使います。 プロキシ
クラスは少なくともパッケージ可視でデフォルトコンストラクタを実装しなければならないことに注意し
てください。すべての永続クラスにこのコンストラクタを推奨します。
ポリモーフィズムのクラスに対してこの方法を適用するときにいくつか考慮することがあります。例:
<class name="Cat" proxy="Cat">
......
<subclass name="DomesticCat">
.....
</subclass>
</class>
第一に、 Cat のインスタンスは Dom esticCat にキャストできません。たとえ基となるインスタンスが
Dom esticCat であったとしてもです:
Cat cat = (Cat) session.load(Cat.class, id);
the db)
if ( cat.isDomesticCat() ) {
proxy
DomesticCat dc = (DomesticCat) cat;
....
}
第二に、プロキシの == は成立しないことがあります。
220
// instantiate a proxy (does not hit
// hit the db to initialize the
// Error!
第19章 パフォーマンスの改善
Cat cat = (Cat) session.load(Cat.class, id);
// instantiate a Cat proxy
DomesticCat dc =
(DomesticCat) session.load(DomesticCat.class, id); // acquire new
DomesticCat proxy!
System.out.println(cat==dc);
// false
しかし、これは見かけほど悪い状況というわけではありません。たとえ異なったプロキシオブジェクトへ
の二つの参照があったとしても、基となるインスタンスは同じオブジェクトです:
cat.setWeight(11.0); // hit the db to initialize the proxy
System.out.println( dc.getWeight() ); // 11.0
第三に、 final クラスや final メソッドを持つクラスに CGLIB プロキシを使えません。
最後に、もし永続オブジェクトのインスタンス化時 (例えば、初期化処理やデフォルトコンストラクタの
中で) になんらかのリソースが必要となるなら、そのリソースもまたプロキシを通して取得されます。実
際には、プロキシクラスは永続クラスのサブクラスです。
T hese problems are all due to fundamental limitations in Java's single inheritance model. If you wish to
avoid these problems your persistent classes must each implement an interface that declares its
business methods. You should specify these interfaces in the mapping file. eg.
<class name="CatImpl" proxy="Cat">
......
<subclass name="DomesticCatImpl" proxy="DomesticCat">
.....
</subclass>
</class>
CatIm pl は Cat インターフェースを実装するのに対し、 Dom esticCatIm pl は Dom esticCat を実
装します。すると、 load() や iterate() は、 Cat や Dom esticCat のインスタンスのプロキシを返
します。( list() は通常はプロキシを返さないことに注意してください。)
Cat cat = (Cat) session.load(CatImpl.class, catid);
Iterator iter = session.iterate("from CatImpl as cat where cat.name='fritz'");
Cat fritz = (Cat) iter.next();
関連も遅延初期化されます。これはプロパティを Cat 型で宣言しなければならないことを意味します。
CatIm pl ではありません。
プロキシの初期化を 必要としない 操作も存在します。
equals() (永続クラスが equals() をオーバーライドしないとき)
hashCode() (永続クラスが hashCode() をオーバーライドしないとき)
識別子の getter メソッド
Hibernate は equals() や hashCode() をオーバーライドした永続クラスを検出します。
By choosing lazy="no-proxy" instead of the default lazy="proxy", we can avoid the problems
associated with typecasting. However, we will require buildtime bytecode instrumentation, and all
operations will result in immediate proxy initialization.
19.1.4. コレクションとプロキシの初期化
LazyInitializationException は、 Session のスコープ外から初期化していないコレクションや
221
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
プロキシにアクセスされたときに、 Hibernate によってスローされます。すなわち、コレクションやプロ
キシへの参照を持つエンティティが分離された状態の時です。
Session をクローズする前にプロキシやコレクションの初期化を確実に行いたいときがあります。もち
ろん、 cat.getSex() や cat.getKittens().size() などを常に呼び出すことで初期化を強制するこ
とはできます。しかしこれはコードを読む人を混乱させ、汎用的なコードという点からも不便です。
static メソッドの Hibernate.initialize() や Hibernate.isInitialized() は遅延初期化のコ
レクションやプロキシを扱うときに便利な方法をアプリケーションに提供します。
Hibernate.initialize(cat) は、 Session がオープンしている限りは cat プロキシを強制的に初
期化します。 Hibernate.initialize( cat.getKittens() ) は kittens コレクションに対して同
様の効果があります。
別の選択肢として、必要なすべてのコレクションやプロキシがロードされるまで Session をオープンに
しておく方法があります。いくつかのアプリケーションのアーキテクチャでは、特に Hibernate による
データアクセスを行うコードと、それを使うコードが異なるアプリケーションのレイヤーや、物理的に異
なるプロセッサのときには、コレクションが初期化されるときに Session がオープンしていることを保
証する問題があります。この問題に対しては2つの基本的な方法があります:
In a web-based application, a servlet filter can be used to close the Session only at the very end of
a user request, once the rendering of the view is complete (the Open Session in View pattern). Of
course, this places heavy demands on the correctness of the exception handling of your application
infrastructure. It is vitally important that the Session is closed and the transaction ended before
returning to the user, even when an exception occurs during rendering of the view. See the Hibernate
Wiki for examples of this "Open Session in View" pattern.
In an application with a separate business tier, the business logic must "prepare" all collections that
will be needed by the web tier before returning. T his means that the business tier should load all the
data and return all the data already initialized to the presentation/web tier that is required for a
particular use case. Usually, the application calls Hibernate.initialize() for each collection
that will be needed in the web tier (this call must occur before the session is closed) or retrieves the
collection eagerly using a Hibernate query with a FET CH clause or a FetchMode.JOIN in
Criteria. T his is usually easier if you adopt the Command pattern instead of a Session Facade.
初期化されていないコレクション(もしくは他のプロキシ)にアクセスする前に、 m erge() や
lock() を使って新しい Session に以前にロードされたオブジェクトを追加することも出来ます。ア
ドホックなトランザクションのセマンティクスを導入したので、 Hibernate はこれを自動的に行わ
ず、 行うべきでもありません 。
Sometimes you don't want to initialize a large collection, but still need some information about it (like its
size) or a subset of the data.
コレクションフィルタを使うことで、初期化せずにコレクションのサイズを取得することが出来ます:
( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0)
).intValue()
createFilter() メソッドは、コレクション全体を初期化する必要なしに、コレクションのサブセット
を復元するために効果的に使えます:
s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();
19.1.5. バッチフェッチの使用
Hibernate はバッチフェッチを効率的に使用できます。一つのプロキシ(もしくはコレクション)がアク
セスされると、 Hibernate はいくつかの初期化していないプロキシをロードすることができます。バッチ
222
第19章 パフォーマンスの改善
フェッチは遅延セレクトフェッチ戦略に対する最適化です。バッチフェッチの調整には2つの方法があり
ます。クラスレベルとコレクションレベルです。
Batch fetching for classes/entities is easier to understand. Imagine you have the following situation at
runtime: You have 25 Cat instances loaded in a Session, each Cat has a reference to its owner, a
Person. T he Person class is mapped with a proxy, lazy="true". If you now iterate through all cats
and call getOwner() on each, Hibernate will by default execute 25 SELECT statements, to retrieve the
proxied owners. You can tune this behavior by specifying a batch-size in the mapping of Person:
<class name="Person" batch-size="10">...</class>
Hibernate はクエリを3回だけを実行するようになります。パターンは 10, 10, 5 です。
コレクションのバッチフェッチも有効にすることが出来ます。例として、それぞれの Person が Cat の
遅延コレクションを持っており、 10 個の Person が Sesssion にロードされたとすると、すべての
Person に対して繰り返し getCats() を呼び出すことで、計10回の SELECT が発生します。もし
Person のマッピングで cats コレクションのバッチフェッチを有効にすれば、 Hibernate はコレクショ
ンの事前フェッチが出来ます。
<class name="Person">
<set name="cats" batch-size="3">
...
</set>
</class>
batch-size が 8 なので、 Hibernate は 4 回の SELECT で 3 個、 3 個、 3 個、 1 個をロードします。
繰り返すと、属性の値は特定の Session の中の初期化されていないコレクションの期待数に依存しま
す。
コレクションのバッチフェッチはアイテムのネストしたツリー、すなわち、代表的な部品表のパターンが
ある場合に特に有用です。(しかし、読み込みが多いツリーでは ネストした set や 具体化したパス がよ
りよい選択になります。)
19.1.6. サブセレクトフェッチの使用
一つの遅延コレクションや単一値プロキシがフェッチされなければいけないとき、 Hibernate はそれらす
べてをロードし、サブセレクトのオリジナルクエリが再度実行されます。これはバッチフェッチと同じ方
法で動き、少しずつのロードは行いません。
19.1.7. 遅延プロパティフェッチの使用
Hibernate3 はプロパティごとの遅延フェッチをサポートしています。この最適化手法は グループの
フェッチ としても知られています。これはほとんど要望から出た機能であることに注意してください。実
際には列読み込みの最適化よりも、行読み込みの最適化が非常に重要です。しかし、クラスのいくつかの
プロパティだけを読み込むことは、既存のテーブルが何百もの列を持ち、データモデルを改善できないな
どの極端な場合には有用です。
遅延プロパティ読み込みを有効にするには、対象のプロパティのマッピングで lazy 属性をセットしてく
ださい:
223
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="Document">
<id name="id">
<generator class="native"/>
</id>
<property name="name" not-null="true" length="50"/>
<property name="summary" not-null="true" length="200" lazy="true"/>
<property name="text" not-null="true" length="2000" lazy="true"/>
</class>
遅延プロパティ読み込みはビルド時のバイトコード組み込みを必要とします。もし永続クラスに組み込み
がされていないなら、 Hibernate は黙って遅延プロパティの設定を無視して、即時フェッチに戻します。
バイトコード組み込みは以下の Ant タスクを使ってください:
<target name="instrument" depends="compile">
<taskdef name="instrument"
classname="org.hibernate.tool.instrument.InstrumentTask">
<classpath path="${jar.path}"/>
<classpath path="${classes.dir}"/>
<classpath refid="lib.class.path"/>
</taskdef>
<instrument verbose="true">
<fileset dir="${testclasses.dir}/org/hibernate/auction/model">
<include name="*.class"/>
</fileset>
</instrument>
</target>
不要な列を読み込まないための、別の(よりよい?)方法は、少なくとも読み込みのみのトランザクショ
ンにおいては、 HQL や Criteria クエリの射影機能を使うことです。この方法はビルド時のバイトコード組
み込みが不要になり、より良い解決方法です。
HQL で fetch all properties を使うことで、普通どおりのプロパティの即時フェッチングを強制す
ることが出来ます。
19.2. 第 2レ ベ ル キ ャ ッ シ ュ
Hibernate の Session は永続データのトランザクションレベルのキャッシュです。 class-by-class と
collection-by-collection ごとの、クラスタレベルや JVM レベル ( SessionFactory レベル)のキャッ
シュを設定することが出来ます。クラスタ化されたキャッシュにつなぐことさえ出来ます。しかし注意し
てください。キャッシュは他のアプリケーションによる永続層の変更を考慮しません(キャッシュデータ
を定期的に期限切れにする設定は出来ます)。
You have the option to tell Hibernate which caching implementation to use by specifying the name of a
class that implements org.hibernate.cache.CacheProvider using the property
hibernate.cache.provider_class. Hibernate comes bundled with a number of built-in
integrations with open-source cache providers (listed below); additionally, you could implement your own
and plug it in as outlined above. Note that versions prior to 3.2 defaulted to use EhCache as the default
cache provider; that is no longer the case as of 3.2.
224
第19章 パフォーマンスの改善
表 19.1 キャッシュプロバイダ
キャッシュ
プロバイダクラス
タイプ
クラスタ
セーフ
Hashtable(
製品用とし
て意図して
いません)
org.hibernate.cache.HashtableCa
cheProvider
メモリ
EHCache
org.hibernate.cache.EhCacheProv メモリ、
ディスク
ider
OSCache
org.hibernate.cache.OSCacheProv メモリ、
ディスク
ider
SwarmCach
e
org.hibernate.cache.Swarm Cache
Provider
クラスタ
(ip マルチ
キャスト)
yes(クラス
タ無効化)
JBoss
T reeCache
org.hibernate.cache.T reeCacheP
rovider
クラスタ
(ip マルチ
キャス
ト)、トラ
ンザクショ
ナル
yes(複製)
クエリ
キャッシュ
のサポート
yes
yes
yes
yes(時刻同
期が必要)
19.2.1. キャッシュのマッピング
T he <cache> element of a class or collection mapping has the following form:
<cache
usage="transactional|read-write|nonstrict-read-write|read-only"
region="RegionName"
include="all|non-lazy"
/>
usage (必須) キャッシング戦略を指定します: transactional、 read-write、 nonstrictread-write または read-only
region (オプション、クラスまたはコレクションのロールネームのデフォルト) 2次レベルのキャッシュ
領域の名前を指定します
include (optional, defaults to all) non-lazy specifies that properties of the entity mapped with
lazy="true" may not be cached when attribute-level lazy fetching is enabled
Alternatively (preferrably?), you may specify <class-cache> and <collection-cache> elements in
hibernate.cfg.xm l.
usage 属性は キャッシュの並列性戦略 を指定します。
19.2.2. read only 戦略
If your application needs to read but never modify instances of a persistent class, a read-only cache
may be used. T his is the simplest and best performing strategy. It's even perfectly safe for use in a
cluster.
225
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="eg.Immutable" mutable="false">
<cache usage="read-only"/>
....
</class>
19.2.3. read/write 戦略
アプリケーションがデータを更新する必要があるなら、 read-write キャッシュが適当かもしれませ
ん。このキャッシュ戦略は、シリアライザブルなトランザクション分離レベルが要求されるなら、決して
使うべきではありません。もしキャッシュが JT A 環境で使われるなら、 JT A T ransactionManager
を取得するための方法を示す hibernate.transaction.m anager_lookup_class プロパティを指
定しなければなりません。他の環境では、 Session.close() や Session.disconnect() が呼ばれ
たときに、確実にトランザクションが完了していなければなりません。もしクラスタでこの戦略を使いた
いなら、基となるキャッシュの実装がロックをサポートしていることを保証しなければなりません。組み
込みのキャッシュプロバイダは サポートしていません 。
<class name="eg.Cat" .... >
<cache usage="read-write"/>
....
<set name="kittens" ... >
<cache usage="read-write"/>
....
</set>
</class>
19.2.4. 厳密ではない read/write 戦略
アプリケーションがたまにしかデータを更新する必要はなく(すなわち二つのトランザクションが同時に
同じアイテムを更新しようとすることはほとんど起こらない)、厳密なトランザクション分離が要求され
ないなら、 nonstrict-read-write キャッシュが適当かもしれません。もしキャッシュが JT A 環境で
使われるなら、 hibernate.transaction.m anager_lookup_class を指定しなければなりませ
ん。他の環境では、 Session.close() や Session.disconnect() が呼ばれたときに、確実にトラ
ンザクションが完了していなければなりません。
19.2.5. transactional 戦略
transactional キャッシュ戦略は JBoss T reeCache のような完全なトランザクショナルキャッシュプ
ロバイダのサポートを提供します。このようなキャッシュは JT A 環境でのみ使用可能で、
hibernate.transaction.m anager_lookup_class を指定しなければなりません。
すべての同時並行性キャッシュ戦略をサポートしているキャッシュプロバイダはありません。以下の表は
どのプロバイダがどの同時並列性戦略に対応するかを表しています。
226
第19章 パフォーマンスの改善
表 19.2 同時並行性キャッシュ戦略のサポート
キャッシュ
read-only
厳密ではない
read-write
read-write
Hashtable(製品
用として意図して
いません)
yes
yes
yes
EHCache
yes
yes
yes
OSCache
yes
yes
yes
SwarmCache
yes
yes
JBoss T reeCache
yes
transactional
yes
19.3. キ ャ ッ シ ュ の 管 理
オブジェクトを save() 、 update() 、 saveOrUpdate() に渡すとき、そして load() 、 get() 、
list() 、 iterate() 、 scroll() を使ってオブジェクトを復元するときには常に、そのオブジェクト
は Session の内部キャッシュに追加されます。
次に flush() が呼ばれると、オブジェクトの状態はデータベースと同期化されます。もしこの同期が起
こることを望まないときや、膨大な数のオブジェクトを処理していてメモリを効率的に扱う必要があると
きは、 evict() メソッドを使って一次キャッシュからオブジェクトやコレクションを削除することが出
来ます。
ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge
result set
while ( cats.next() ) {
Cat cat = (Cat) cats.get(0);
doSomethingWithACat(cat);
sess.evict(cat);
}
Session はインスタンスがセッションキャッシュに含まれるかどうかを判断するための contains() メ
ソッドも提供します。
すべてのオブジェクトをセッションキャッシュから完全に取り除くには、 Session.clear() を呼び出
してください。
二次キャッシュのために、 SessionFactory にはインスタンス、クラス全体、コレクションのインスタ
ンス、コレクション全体をキャッシュから削除するためのメソッドがそれぞれ定義されています。
sessionFactory.evict(Cat.class, catId); //evict a particular Cat
sessionFactory.evict(Cat.class); //evict all Cats
sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular
collection of kittens
sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections
CacheMode は特定のセッションが二次キャッシュとどのように相互作用するかを指定します。
CacheMode.NORMAL - アイテムの読み込みと書き込みで二次キャッシュを使います
CacheMode.GET - read items from the second-level cache, but don't write to the second-level
cache except when updating data
227
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
CacheMode.PUT - write items to the second-level cache, but don't read from the second-level
cache
CacheMode.REFRESH - write items to the second-level cache, but don't read from the second-level
cache, bypass the effect of hibernate.cache.use_m inim al_puts, forcing a refresh of the
second-level cache for all items read from the database
二次キャッシュの内容やクエリキャッシュ領域を見るために、 Statistics API を使ってください:
Map cacheEntries = sessionFactory.getStatistics()
.getSecondLevelCacheStatistics(regionName)
.getEntries();
You'll need to enable statistics, and, optionally, force Hibernate to keep the cache entries in a more
human-understandable format:
hibernate.generate_statistics true
hibernate.cache.use_structured_entries true
19.4. ク エ リ キ ャ ッ シ ュ
クエリのリザルトセットもキャッシュ出来ます。これは同じパラメータで何度も実行されるクエリに対し
てのみ有用です。クエリキャッシュを使うには、まず設定で有効にしなくてはなりません:
hibernate.cache.use_query_cache true
この設定は新たに二つのキャッシュ領域の作成を行います。一つはクエリのリザルトセットのキャッシュ
( org.hibernate.cache.StandardQueryCache )を保持し、もう1つはクエリ可能なテーブルへ
の最新の更新タイムスタンプ ( org.hibernate.cache.UpdateT im estam psCache )を保持しま
す。クエリキャッシュはリザルトセットの実際の要素の状態はキャッシュしないことに注意してくださ
い。キャッシュするのは識別子の値と、値型の結果のみです。そのため、クエリキャッシュは常に二次
キャッシュと一緒に使うべきです。
ほとんどのクエリはキャッシュの恩恵を受けないので、デフォルトではクエリはキャッシュされません。
キャッシュを有効にするには、 Query.setCacheable(true) を呼び出してください。そうすればクエ
リが既存のキャッシュ結果を探し、クエリ実行時にその結果をキャッシュに追加するようになります。
クエリキャッシュの破棄ポリシーを細かく制御したいときは、 Query.setCacheRegion() を呼び出し
て特定のクエリに対するキャッシュ領域を指定することが出来ます。
List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
.setEntity("blogger", blogger)
.setMaxResults(15)
.setCacheable(true)
.setCacheRegion("frontpages")
.list();
クエリが自身のクエリキャッシュ領域のリフレッシュを強制しなければならないなら、
Query.setCacheMode(CacheMode.REFRESH) を呼び出すべきです。これは元となるデータが別の
プロセスによって更新されたり(すなわち Hibernate を通じて更新されない)、アプリケーションに特定
のクエリリザルトセットを選択してリフレッシュさせる場合に特に有用です。さらに有用なもう一つの方
法は、 SessionFactory.evictQueries() によってクエリキャッシュ領域を消去することです。
228
第19章 パフォーマンスの改善
19.5. コ レ ク シ ョ ン の パ フ ォ ー マ ン ス の 理 解
We've already spent quite some time talking about collections. In this section we will highlight a couple
more issues about how collections behave at runtime.
19.5.1. 分類
Hibernate は3つの基本的なコレクションの種類を定義しています:
値のコレクション
一対多関連
多対多関連
この分類はさまざまなテーブルや外部キー関連を区別しますが、私たちが知る必要のある関連モデルにつ
いてほとんどなにも教えてくれません。関連構造やパフォーマンスの特徴を完全に理解するには、
Hibernate がコレクションの行を更新、削除するために使う主キーの構造もまた考えなければなりませ
ん。これは以下の分類を提示します。
インデックス付きコレクション
set
bag
All indexed collections (maps, lists, arrays) have a primary key consisting of the <key> and <index>
columns. In this case collection updates are usually extremely efficient - the primary key may be
efficiently indexed and a particular row may be efficiently located when Hibernate tries to update or
delete it.
Sets have a primary key consisting of <key> and element columns. T his may be less efficient for some
types of collection element, particularly composite elements or large text or binary fields; the database
may not be able to index a complex primary key as efficently. On the other hand, for one to many or many
to many associations, particularly in the case of synthetic identifiers, it is likely to be just as efficient.
(Side-note: if you want Schem aExport to actually create the primary key of a <set> for you, you must
declare all columns as not-null="true".)
<idbag> mappings define a surrogate key, so they are always very efficient to update. In fact, they are
the best case.
bag は最悪のケースです。 bag は要素の値の重複が可能で、インデックスカラムを持たないため、主キー
は定義されないかもしれません。 Hibernate には重複した行を区別する方法がありません。 Hibernate は
この問題の解決のために、変更があったときには常に完全な削除(一つの DELET E による)を行い、コレ
クションの再作成を行います。これは非常に非効率的かもしれません。
Note that for a one-to-many association, the "primary key" may not be the physical primary key of the
database table - but even in this case, the above classification is still useful. (It still reflects how
Hibernate "locates" individual rows of the collection.)
19.5.2. 更新にもっとも効率的なコレクション list、 map、 idbag、 set
上での議論から、インデックス付きコレクションと(普通の) set は要素の追加、削除、更新でもっとも
効率的な操作が出来ることは明らかです。
T here is, arguably, one more advantage that indexed collections have over sets for many to many
associations or collections of values. Because of the structure of a Set, Hibernate doesn't ever UPDAT E
a row when an element is "changed". Changes to a Set always work via INSERT and DELET E (of
individual rows). Once again, this consideration does not apply to one to many associations.
229
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
After observing that arrays cannot be lazy, we would conclude that lists, maps and idbags are the most
performant (non-inverse) collection types, with sets not far behind. Sets are expected to be the most
common kind of collection in Hibernate applications. T his is because the "set" semantics are most
natural in the relational model.
However, in well-designed Hibernate domain models, we usually see that most collections are in fact
one-to-many associations with inverse="true". For these associations, the update is handled by the
many-to-one end of the association, and so considerations of collection update performance simply do
not apply.
19.5.3. inverse コレクションにもっとも最適な bag と list
Just before you ditch bags forever, there is a particular case in which bags (and also lists) are much
more performant than sets. For a collection with inverse="true" (the standard bidirectional one-tomany relationship idiom, for example) we can add elements to a bag or list without needing to initialize
(fetch) the bag elements! T his is because Collection.add() or Collection.addAll() must
always return true for a bag or List (unlike a Set). T his can make the following common code much
faster.
Parent p = (Parent) sess.load(Parent.class, id);
Child c = new Child();
c.setParent(p);
p.getChildren().add(c); //no need to fetch the collection!
sess.flush();
19.5.4. 一括削除
Occasionally, deleting collection elements one by one can be extremely inefficient. Hibernate isn't
completely stupid, so it knows not to do that in the case of an newly-empty collection (if you called
list.clear(), for example). In this case, Hibernate will issue a single DELET E and we are done!
サイズ20のコレクションに一つの要素を追加し、それから二つの要素を削除するとします。 Hibernate は
一つの INSERT 文と二つの DELET E 文を発行します (コレクションが bag でなければ)。これは確かに
望ましい動作です。
しかし、18個の要素を削除して2つを残し、それから3つ新しい要素を追加するとします。このとき二つの
方法があります。
18行を一つ一つ削除して、3行を追加する
コレクション全体を削除( DELET E の SQL を一回)し、そして5つの要素すべてを(一つずつ)追加
する
Hibernate isn't smart enough to know that the second option is probably quicker in this case. (And it
would probably be undesirable for Hibernate to be that smart; such behaviour might confuse database
triggers, etc.)
幸いにも、元のコレクションを捨て(つまり参照をやめて)、現在の要素をすべて持つ新しいコレクショ
ンのインスタンスを返すことで、いつでもこの振る舞い(2番目の戦略)を強制することが出来ます。時
にこれはとても便利で強力です。
Of course, one-shot-delete does not apply to collections mapped inverse="true".
19.6. パ フ ォ ー マ ン ス の モ ニ タ リ ン グ
最適化はモニタリングやパフォーマンスを示す数値がなければ十分に行えません。 Hibernate は内部処理
230
第19章 パフォーマンスの改善
のすべての範囲の数値を提供します。 Hibernate の統計情報は SessionFactory 単位で取得可能です。
19.6.1. SessionFactory のモニタリング
SessionFactory のメトリクスにアクセスするには2つの方法があります。最初の方法は、
sessionFactory.getStatistics() を呼び出し、自分で Statistics の読み込みや表示を行いま
す。
StatisticsService MBean を有効にしていれば、 Hibernate は JMX を使ってメトリクスを発行する
こともできます。1つの MBean をすべての SessionFactory に対して有効にするか、 SessionFactory
ごとに一つの MBean を有効にすることが出来ます。最小限の設定例である以下のコードを見てください:
// MBean service registration for a specific SessionFactory
Hashtable tb = new Hashtable();
tb.put("type", "statistics");
tb.put("sessionFactory", "myFinancialApp");
ObjectName on = new ObjectName("hibernate", tb); // MBean object name
StatisticsService stats = new StatisticsService(); // MBean implementation
stats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactory
server.registerMBean(stats, on); // Register the Mbean on the server
// MBean service registration for all SessionFactory's
Hashtable tb = new Hashtable();
tb.put("type", "statistics");
tb.put("sessionFactory", "all");
ObjectName on = new ObjectName("hibernate", tb); // MBean object name
StatisticsService stats = new StatisticsService(); // MBean implementation
server.registerMBean(stats, on); // Register the MBean on the server
T ODO: T his doesn't make sense: In the first case, we retrieve and use the MBean directly. In the second
one, we must give the JNDI name in which the session factory is held before using it. Use
hibernateStatsBean.setSessionFactoryJNDINam e("m y/JNDI/Nam e")
SessionFactory に対してモニタリングの開始(終了)を行うことが出来ます。
設定時には、 hibernate.generate_statistics を false にします
実行時に、 sf.getStatistics().setStatisticsEnabled(true) または
hibernateStatsBean.setStatisticsEnabled(true) を呼び出します
統計は clear() メソッドを使って手動でリセットすることが出来ます。サマリは logSum m ary() メ
ソッドを使って logger に送ることが出来ます(info レベルです)。
19.6.2. メトリクス
多くのものがあります。すべての使用可能なカウンタは Statistics インターフェースの API に書かれ
ており、3つの分類があります:
メトリクスは一般的な Session の使い方と関係しています。オープンしたセッションの数が JDBC
コネクションと関連しているのと同じです。
メトリクスは要素、コレクション、クエリやキャッシュなど全体に関係しています(別名はグローバ
ルメトリクスです)。
メトリクスの詳細は特定のエンティティ、コレクション、クエリ、キャッシュ領域に関係していま
す。
231
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
例として、キャッシュのヒット、ヒットミスや、要素、コレクション、クエリの割合、クエリの実行に必
要な平均時間を確認できます。ミリ秒の数値は Java の近似を受けることに注意してください。 Hibernate
は JVM の精度に制限され、プラットフォームによっては10秒単位でしか正確でないかもしれません。
単純な getter はグローバルメトリクス(すなわち特定のエンティティ、コレクション、キャッシュ領域な
どに縛られない)にアクセスするために使います。特定のエンティティ、コレクション、キャッシュ領域
のメトリクスは、それらの名前や、クエリの HQL 、 SQL 表現によってアクセスすることが出来ます。さ
らに詳しい情報は、 Statistics 、 EntityStatistics 、 CollectionStatistics 、
SecondLevelCacheStatistics 、 QueryStatistics API の javadoc を参照してください。以下の
コードは簡単な例です:
Statistics stats = HibernateUtil.sessionFactory.getStatistics();
double queryCacheHitCount = stats.getQueryCacheHitCount();
double queryCacheMissCount = stats.getQueryCacheMissCount();
double queryCacheHitRatio =
queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);
log.info("Query Hit ratio:" + queryCacheHitRatio);
EntityStatistics entityStats =
stats.getEntityStatistics( Cat.class.getName() );
long changes =
entityStats.getInsertCount()
+ entityStats.getUpdateCount()
+ entityStats.getDeleteCount();
log.info(Cat.class.getName() + " changed " + changes + "times"
);
すべてのエンティティ、コレクション、クエリ、キャッシュ領域に対して行う場合は、 getQueries()
、 getEntityNam es()、 getCollectionRoleNam es() 、
getSecondLevelCacheRegionNam es() メソッドでそれぞれの名前のリストを取得することが出来
ます。
232
第20章 ツールセットガイド
第 20章 ツールセットガイド
Hibernate を使ったラウンドトリップエンジニアリングは、 Eclipse プラグインやコマンドラインツー
ル、もちろん Ant タスクを使うことで可能です。
Hibernate Tools は現在、既存データベースのリバースエンジニアリングの Ant タスクに加えて、 Eclipse
IDE のプラグインを含みます:
マッピングエディタ: Hibernate の XML マッピングファイル用のエディタで、自動補完と構文強調表示
をサポートしています。クラス名やプロパティ/フィールド名に対する自動補完もサポートし、通常の
XML エディタよりも強力です。
Console: コンソールはエクリプスの新しいビューです。コンソールコンフィギュレーションのツリー
オーバービューに加えて、永続クラスとその関連の相互作用ビューも得られます。データベースに
HQL を実行し、結果を直接エクリプス上で見ることができます。
開発ウィザード Hibernate の Eclipse ツールはいくつかのウィザードを提供します。ウィザードを使っ
て Hibernate の設定ファイル (cfg.xml) をすばやく生成したり、既存のデータベーススキーマを POJO
のソースファイルと Hibernate のマッピングファイルへと、完全にリバースエンジニアリングするこ
とができます。リバースエンジニアリングウィザードはカスタマイズ可能なテンプレートをサポート
します。
Ant Tasks:
Please refer to the Hibernate Tools package and it's documentation for more information.
However, the Hibernate main package comes bundled with an integrated tool (it can even be used from
"inside" Hibernate on-the-fly): SchemaExport aka hbm 2ddl.
20.1. ス キ ー マ の 自 動 生 成
DDL は Hibernate ユーティリティによりマッピングファイルから生成することができます。生成されたス
キーマはエンティティやコレクションのテーブルに対する参照整合性制約 (主キーと外部キー) を含みま
す。テーブルとシーケンスはマッピングする識別子ジェネレータに対して生成されます。
DDL はベンダー依存なので、このツールを使うときは、 hibernate.dialect プロパティで SQL の 方
言 を指定 しなければなりません 。
まず、生成されるスキーマを改善するように、マッピングファイルをカスタマイズしてください。
20.1.1. スキーマのカスタマイズ
多くの Hibernate のマッピング要素では、オプションの length、 precision、 scale という名の属
性を定義しています。この属性でカラムの長さ、精度、スケールを設定することができます。
<property name="zip" length="5"/>
<property name="balance" precision="12" scale="2"/>
not-null 属性(テーブルのカラムへ NOT NULL 制約を生成する)と unique 属性(テーブルのカラム
へ UNIQUE 制約を生成する)が設定できるタグもあります。
<many-to-one name="bar" column="barId" not-null="true"/>
<element column="serialNumber" type="long" not-null="true" unique="true"/>
233
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
unique-key 属性はカラムをグループ化して一つのキー制約にするために使われます。現在、 uniquekey 属性で指定された値は制約の指定には 使われず 、マッピングファイルでカラムをグループ化するこ
とにのみ使われます。
<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/>
<property name="employeeId" unique-key="OrgEmployee"/>
index 属性はマッピングするカラムを使って生成したインデックスの名前を指定します。複数カラムを1
つのインデックスにグループ化できます。単に、同じインデックス名を指定するだけです。
<property name="lastName" index="CustName"/>
<property name="firstName" index="CustName"/>
foreign-key 属性は、生成された外部キー制約の名前をオーバーライドするために使用できます。
<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>
Many mapping elements also accept a child <colum n> element. T his is particularly useful for mapping
multi-column types:
<property name="name" type="my.customtypes.Name"/>
<column name="last" not-null="true" index="bar_idx" length="30"/>
<column name="first" not-null="true" index="bar_idx" length="20"/>
<column name="initial"/>
</property>
default 属性はカラムのデフォルト値を指定します (マッピングしたクラスの新しいインスタンスを保存
する前に、マッピングしたプロパティへ同じ値を代入すべきです)。
<property name="credits" type="integer" insert="false">
<column name="credits" default="10"/>
</property>
<version name="version" type="integer" insert="false">
<column name="version" default="0"/>
</property>
sql-type 属性で、デフォルトの Hibernate 型から SQL のデータ型へのマッピングをオーバーライドで
きます。
<property name="balance" type="float">
<column name="balance" sql-type="decimal(13,3)"/>
</property>
check 属性でチェック制約を指定することができます。
<property name="foo" type="integer">
<column name="foo" check="foo > 10"/>
</property>
234
第20章 ツールセットガイド
<class name="Foo" table="foos" check="bar < 100.0">
...
<property name="bar" type="float"/>
</class>
表 20.1 まとめ
属性
値
説明
length
数値
カラムの長さ
precision
数値
カラムの DECIMAL 型の精度
(precision)
scale
数値
カラムの DECIMAL 型のスケール
(scale)
not-null
true|false
カラムが null 値を取らないこと
を指定します
unique
true|false
カラムがユニーク制約を持つこ
とを指定します
index
index_nam e
(複数カラムの)インデックスの名
前を指定します
unique-key
unique_key_nam e
複数カラムのユニーク制約の名
前を指定します
foreign-key
foreign_key_nam e
specifies the name of the
foreign key constraint generated
for an association, for a <oneto-one>, <m any-to-one>,
<key>, or <m any-to-m any>
mapping element. Note that
inverse="true" sides will
not be considered by
Schem aExport.
sql-type
SQL colum n type
overrides the default column
type (attribute of <colum n>
element only)
default
SQL 式
カラムのデフォルト値を指定し
ます
check
SQL 式
カラムかテーブルに SQL の
チェック制約を作成します
T he <com m ent> element allows you to specify comments for the generated schema.
<class name="Customer" table="CurCust">
<comment>Current customers only</comment>
...
</class>
<property name="balance">
<column name="bal">
<comment>Balance in USD</comment>
</column>
</property>
235
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
これにより、生成した DDL に com m ent on table や com m ent on colum n 文が書かれます。
20.1.2. ツールの実行
Schem aExport は標準出力に対して DDL スクリプトを書き出し、 DDL 文を実行したりもします。
java -cp hibernate_classpathsorg.hibernate.tool.hbm 2ddl.Schem aExportoptions
mapping_files
表 20.2 Schem aExport のコマンドラインオプション
オプション
説明
--quiet
don't output the script to stdout
--drop
テーブルの削除だけを行います
--create
テーブルの生成のみを行います
--text
don't export to the database
--output=m y_schem a.ddl
DDL スクリプトをファイルに出力します
-nam ing=eg.MyNam ingStra
tegy
Nam ingStrategy を選択
-config=hibernate.cfg.xm
l
XML ファイルから Hibernate の定義情報を読み込みます
-properties=hibernate.pr
operties
ファイルからデータベースのプロパティを読み込みます
--form at
スクリプト内に生成する SQL を読みやすいようにフォーマットしま
す
--delim iter=;
スクリプトの行区切り文字を設定します
アプリケーションに Schem aExport を組み込むこともできます:
Configuration cfg = ....;
new SchemaExport(cfg).create(false, true);
20.1.3. プロパティ
次のように、データベースのプロパティを指定することができます。
as system properties with -D<property>
hibernate.properties ファイル内で
--properties を使って指定したプロパティファイル内で
必要なプロパティは以下のものです:
236
第20章 ツールセットガイド
表 20.3 SchemaExport コネクションプロパティ
プロパティ名
説明
hibernate.connection.dr
iver_class
jdbc のドライバークラス
hibernate.connection.ur
l
jdbc の url
hibernate.connection.us
ernam e
データベースのユーザー
hibernate.connection.pa
ssword
ユーザーパスワード
hibernate.dialect
データベース方言
20.1.4. Ant を使用する
Ant のビルドスクリプトから Schem aExport を呼び出すことができます:
<target name="schemaexport">
<taskdef name="schemaexport"
classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
classpathref="class.path"/>
<schemaexport
properties="hibernate.properties"
quiet="no"
text="no"
drop="no"
delimiter=";"
output="schema-export.sql">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaexport>
</target>
20.1.5. インクリメンタルなスキーマ更新
T he Schem aUpdate tool will update an existing schema with "incremental" changes. Note that
Schem aUpdate depends heavily upon the JDBC metadata API, so it will not work with all JDBC drivers.
java -cp hibernate_classpathsorg.hibernate.tool.hbm 2ddl.Schem aUpdateoptions
mapping_files
237
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
表 20.4 Schem aUpdate のコマンドラインオプション
オプション
説明
--quiet
don't output the script to stdout
--text
don't export the script to the database
-nam ing=eg.MyNam ingStra
tegy
Nam ingStrategy を選択
-properties=hibernate.pr
operties
ファイルからデータベースのプロパティを読み込みます
-config=hibernate.cfg.xm
l
.cfg.xm l ファイルを指定
アプリケーションに Schem aUpdate を組み込むことができます:
Configuration cfg = ....;
new SchemaUpdate(cfg).execute(false);
20.1.6. インクリメンタルなスキーマ更新に対する Ant の使用
Ant スクリプトから Schem aUpdate を呼び出すことができます:
<target name="schemaupdate">
<taskdef name="schemaupdate"
classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
classpathref="class.path"/>
<schemaupdate
properties="hibernate.properties"
quiet="no">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaupdate>
</target>
20.1.7. Schema validation
T he Schem aValidator tool will validate that the existing database schema "matches" your mapping
documents. Note that Schem aValidator depends heavily upon the JDBC metadata API, so it will not
work with all JDBC drivers. T his tool is extremely useful for testing.
java -cp hibernate_classpathsorg.hibernate.tool.hbm 2ddl.Schem aValidatoroptions
mapping_files
238
第20章 ツールセットガイド
表 20.5 Schem aValidator のコマンドラインオプション
オプション
説明
-nam ing=eg.MyNam ingStra
tegy
Nam ingStrategy を選択
-properties=hibernate.pr
operties
ファイルからデータベースのプロパティを読み込みます
-config=hibernate.cfg.xm
l
.cfg.xm l ファイルを指定
Schem aValidator をアプリケーションに組み込むことが出来ます:
Configuration cfg = ....;
new SchemaValidator(cfg).validate();
20.1.8. スキーマのバリデーションに Ant を使用します
Ant スクリプトから Schem aValidator を呼び出せます:
<target name="schemavalidate">
<taskdef name="schemavalidator"
classname="org.hibernate.tool.hbm2ddl.SchemaValidatorTask"
classpathref="class.path"/>
<schemavalidator
properties="hibernate.properties">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaupdate>
</target>
239
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 21章 例: 親 /子供
One of the very first things that new users try to do with Hibernate is to model a parent / child type
relationship. T here are two different approaches to this. For various reasons the most convenient
approach, especially for new users, is to model both Parent and Child as entity classes with a
<one-to-m any> association from Parent to Child. (T he alternative approach is to declare the
Child as a <com posite-elem ent>.) Now, it turns out that default semantics of a one to many
association (in Hibernate) are much less close to the usual semantics of a parent / child relationship
than those of a composite element mapping. We will explain how to use a bidirectional one to many
association with cascades to model a parent / child relationship efficiently and elegantly. It's not at all
difficult!
21.1. コ レ ク シ ョ ン に 関 す る 注 意
Hibernate のコレクションは自身のエンティティの論理的な部分と考えられ、決して包含するエンティ
ティのものではありません。これは致命的な違いです。これは以下のような結果になります:
オブジェクトをコレクションから削除、またはコレクションに追加するとき、コレクションのオー
ナーのバージョン番号はインクリメントされます。
もしコレクションから削除されたオブジェクトが値型のインスタンス(例えばコンポジットエレメン
ト) だったならば、そのオブジェクトは永続的ではなくなり、その状態はデータベースから完全に削除
されます。同じように、値型のインスタンスをコレクションに追加すると、その状態はすぐに永続的
になります。
一方、もしエンティティがコレクション(一対多または多対多関連) から削除されても、デフォルトで
はそれは削除されません。この動作は完全に一貫しています。すなわち、他のエンティティの内部状
態を変更しても、関連するエンティティが消滅すべきではないということです。同様に、エンティ
ティがコレクションに追加されても、デフォルトではそのエンティティは永続的にはなりません。
その代わりに、デフォルトの動作では、エンティティをコレクションに追加すると単に二つのエンティ
ティ間のリンクを作成し、一方エンティティを削除するとリンクも削除します。これはすべてのケースに
おいて非常に適切です。これが適切でないのは親/子関係の場合です。この場合子供の生存は親のライフサ
イクルに制限されるからです。
21.2. 双 方 向 一 対 多
Suppose we start with a simple <one-to-m any> association from Parent to Child.
<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
以下のコードを実行すると、
Parent p = .....;
Child c = new Child();
p.getChildren().add(c);
session.save(c);
session.flush();
Hibernate は二つの SQL 文を発行します:
c に対するレコードを生成する INSERT
24 0
第21章 例: 親/子供
p から c へのリンクを作成する UPDAT E
T his is not only inefficient, but also violates any NOT NULL constraint on the parent_id column. We
can fix the nullability constraint violation by specifying not-null="true" in the collection mapping:
<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>
しかしこの解決策は推奨できません。
この動作の根本的な原因は、 p から c へのリンク(外部キー parent_id) は Child オブジェクトの状
態の一部とは考えられず、そのため INSERT によってリンクが生成されないことです。ですから、解決策
はリンクを Child マッピングの一部にすることです。
<many-to-one name="parent" column="parent_id" not-null="true"/>
(また Child クラスに parent プロパティを追加する必要があります。)
それでは Child エンティティがリンクの状態を制御するようになったので、コレクションがリンクを更
新しないようにしましょう。それには inverse 属性を使います。
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
以下のコードを使えば、新しい Child を追加することができます。
Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
c.setParent(p);
p.getChildren().add(c);
session.save(c);
session.flush();
これにより、 SQL の INSERT 文が一つだけが発行されるようになりました。
もう少し強化するには、 Parent の addChild() メソッドを作成します。
public void addChild(Child c) {
c.setParent(this);
children.add(c);
}
Child を追加するコードはこのようになります。
Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.save(c);
session.flush();
24 1
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
21.3. ラ イ フ サ イ ク ル の カ ス ケ ー ド
明示的に save() をコールするのはまだ煩わしいものです。これをカスケードを使って対処します。
<set name="children" inverse="true" cascade="all">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
これにより先ほどのコードをこのように単純化します
Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.flush();
Similarly, we don't need to iterate over the children when saving or deleting a Parent. T he following
removes p and all its children from the database.
Parent p = (Parent) session.load(Parent.class, pid);
session.delete(p);
session.flush();
しかしこのコードは
Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
c.setParent(null);
session.flush();
データベースから c を削除しません。 p へのリンクを削除する(そしてこのケースでは NOT NULL 制約
違反を引き起こす)だけです。 Child の delete() を明示する必要があります。
Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
session.delete(c);
session.flush();
Now, in our case, a Child can't really exist without its parent. So if we remove a Child from the
collection, we really do want it to be deleted. For this, we must use cascade="all-delete-orphan".
<set name="children" inverse="true" cascade="all-delete-orphan">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
Note: even though the collection mapping specifies inverse="true", cascades are still processed by
iterating the collection elements. So if you require that an object be saved, deleted or updated by
cascade, you must add it to the collection. It is not enough to simply call setParent().
21.4. カ ス ケ ー ド と unsaved-value
24 2
第21章 例: 親/子供
Suppose we loaded up a Parent in one Session, made some changes in a UI action and wish to
persist these changes in a new session by calling update(). T he Parent will contain a collection of
childen and, since cascading update is enabled, Hibernate needs to know which children are newly
instantiated and which represent existing rows in the database. Lets assume that both Parent and
Child have genenerated identifier properties of type Long. Hibernate will use the identifier and
version/timestamp property value to determine which of the children are new. (See 「自動的な状態検
出」.) In Hibernate3, it is no longer necessary to specify an unsaved-value explicitly.
以下のコードは parent と child を更新し、 newChild を挿入します。
//parent and child were both loaded in a previous session
parent.addChild(child);
Child newChild = new Child();
parent.addChild(newChild);
session.update(parent);
session.flush();
Well, that's all very well for the case of a generated identifier, but what about assigned identifiers and
composite identifiers? T his is more difficult, since Hibernate can't use the identifier property to
distinguish between a newly instantiated object (with an identifier assigned by the user) and an object
loaded in a previous session. In this case, Hibernate will either use the timestamp or version property, or
will actually query the second-level cache or, worst case, the database, to see if the row exists.
21.5. 結 論
ここではかなりの量を要約したので、最初の頃は混乱しているように思われるかもしれません。しかし実
際は、すべて非常に良く動作します。ほとんどの Hibernate アプリケーションでは、多くの場面で親子パ
ターンを使用します。
We mentioned an alternative in the first paragraph. None of the above issues exist in the case of
<com posite-elem ent> mappings, which have exactly the semantics of a parent / child relationship.
Unfortunately, there are two big limitations to composite element classes: composite elements may not
own collections, and they should not be the child of any entity other than the unique parent.
24 3
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 22章 例 : Weblog アプリケーション
22.1. 永 続 ク ラ ス
永続クラスがウェブログと、ウェブログに掲示された項目を表しています。それらは通常の親子関係とし
てモデリングされますが、 set ではなく順序を持った bag を使用することにします。
package eg;
import java.util.List;
public class Blog {
private Long _id;
private String _name;
private List _items;
public Long getId() {
return _id;
}
public List getItems() {
return _items;
}
public String getName() {
return _name;
}
public void setId(Long long1) {
_id = long1;
}
public void setItems(List list) {
_items = list;
}
public void setName(String string) {
_name = string;
}
}
24 4
第22章 例: Weblog アプリケーション
package eg;
import java.text.DateFormat;
import java.util.Calendar;
public class BlogItem {
private Long _id;
private Calendar _datetime;
private String _text;
private String _title;
private Blog _blog;
public Blog getBlog() {
return _blog;
}
public Calendar getDatetime() {
return _datetime;
}
public Long getId() {
return _id;
}
public String getText() {
return _text;
}
public String getTitle() {
return _title;
}
public void setBlog(Blog blog) {
_blog = blog;
}
public void setDatetime(Calendar calendar) {
_datetime = calendar;
}
public void setId(Long long1) {
_id = long1;
}
public void setText(String string) {
_text = string;
}
public void setTitle(String string) {
_title = string;
}
}
22.2. Hibernate の マ ッ ピ ン グ
XML マッピングは、今ではとても簡単なはずです。
24 5
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class
name="Blog"
table="BLOGS">
<id
name="id"
column="BLOG_ID">
<generator class="native"/>
</id>
<property
name="name"
column="NAME"
not-null="true"
unique="true"/>
<bag
name="items"
inverse="true"
order-by="DATE_TIME"
cascade="all">
<key column="BLOG_ID"/>
<one-to-many class="BlogItem"/>
</bag>
</class>
</hibernate-mapping>
24 6
第22章 例: Weblog アプリケーション
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class
name="BlogItem"
table="BLOG_ITEMS"
dynamic-update="true">
<id
name="id"
column="BLOG_ITEM_ID">
<generator class="native"/>
</id>
<property
name="title"
column="TITLE"
not-null="true"/>
<property
name="text"
column="TEXT"
not-null="true"/>
<property
name="datetime"
column="DATE_TIME"
not-null="true"/>
<many-to-one
name="blog"
column="BLOG_ID"
not-null="true"/>
</class>
</hibernate-mapping>
22.3. Hibernate の コ ー ド
以下のクラスは、 Hibernate でこれらのクラスを使ってできるいくつかのことを示しています。
24 7
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
package eg;
import
import
import
import
java.util.ArrayList;
java.util.Calendar;
java.util.Iterator;
java.util.List;
import
import
import
import
import
import
import
org.hibernate.HibernateException;
org.hibernate.Query;
org.hibernate.Session;
org.hibernate.SessionFactory;
org.hibernate.Transaction;
org.hibernate.cfg.Configuration;
org.hibernate.tool.hbm2ddl.SchemaExport;
public class BlogMain {
private SessionFactory _sessions;
public void configure() throws HibernateException {
_sessions = new Configuration()
.addClass(Blog.class)
.addClass(BlogItem.class)
.buildSessionFactory();
}
public void exportTables() throws HibernateException {
Configuration cfg = new Configuration()
.addClass(Blog.class)
.addClass(BlogItem.class);
new SchemaExport(cfg).create(true, true);
}
public Blog createBlog(String name) throws HibernateException {
Blog blog = new Blog();
blog.setName(name);
blog.setItems( new ArrayList() );
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.persist(blog);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
public BlogItem createBlogItem(Blog blog, String title, String text)
throws HibernateException {
BlogItem item = new BlogItem();
24 8
第22章 例: Weblog アプリケーション
item.setTitle(title);
item.setText(text);
item.setBlog(blog);
item.setDatetime( Calendar.getInstance() );
blog.getItems().add(item);
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.update(blog);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return item;
}
public BlogItem createBlogItem(Long blogid, String title, String text)
throws HibernateException {
BlogItem item = new BlogItem();
item.setTitle(title);
item.setText(text);
item.setDatetime( Calendar.getInstance() );
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Blog blog = (Blog) session.load(Blog.class, blogid);
item.setBlog(blog);
blog.getItems().add(item);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return item;
}
public void updateBlogItem(BlogItem item, String text)
throws HibernateException {
item.setText(text);
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.update(item);
24 9
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
}
public void updateBlogItem(Long itemid, String text)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
item.setText(text);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
}
public List listAllBlogNamesAndItemCounts(int max)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
List result = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"select blog.id, blog.name, count(blogItem) " +
"from Blog as blog " +
"left outer join blog.items as blogItem " +
"group by blog.name, blog.id " +
"order by max(blogItem.datetime)"
);
q.setMaxResults(max);
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
250
第22章 例: Weblog アプリケーション
public Blog getBlogAndAllItems(Long blogid)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
Blog blog = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"from Blog as blog " +
"left outer join fetch blog.items " +
"where blog.id = :blogid"
);
q.setParameter("blogid", blogid);
blog = (Blog) q.uniqueResult();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
public List listBlogsAndRecentItems() throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
List result = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"from Blog as blog " +
"inner join blog.items as blogItem " +
"where blogItem.datetime > :minDate"
);
Calendar cal = Calendar.getInstance();
cal.roll(Calendar.MONTH, false);
q.setCalendar("minDate", cal);
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
}
251
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
第 23章 例: いろいろなマッピング
この章では、より複雑な関連のマッピングをいくつか紹介します。
23.1. 雇 用 者 /従 業 員
Em ployer と Em ployee の関係を表す以下のモデルは、関連の表現に実際のエンティティクラス (
Em ploym ent ) を使います。なぜなら、同じ2つのパーティに複数の期間雇用されるということがあり
えるからです。お金の値と従業員の名前をモデル化するためにコンポーネントを使っています。
マッピングドキュメントの一例です:
252
第23章 例: いろいろなマッピング
<hibernate-mapping>
<class name="Employer" table="employers">
<id name="id">
<generator class="sequence">
<param name="sequence">employer_id_seq</param>
</generator>
</id>
<property name="name"/>
</class>
<class name="Employment" table="employment_periods">
<id name="id">
<generator class="sequence">
<param name="sequence">employment_id_seq</param>
</generator>
</id>
<property name="startDate" column="start_date"/>
<property name="endDate" column="end_date"/>
<component name="hourlyRate" class="MonetaryAmount">
<property name="amount">
<column name="hourly_rate" sql-type="NUMERIC(12, 2)"/>
</property>
<property name="currency" length="12"/>
</component>
<many-to-one name="employer" column="employer_id" not-null="true"/>
<many-to-one name="employee" column="employee_id" not-null="true"/>
</class>
<class name="Employee" table="employees">
<id name="id">
<generator class="sequence">
<param name="sequence">employee_id_seq</param>
</generator>
</id>
<property name="taxfileNumber"/>
<component name="name" class="Name">
<property name="firstName"/>
<property name="initial"/>
<property name="lastName"/>
</component>
</class>
</hibernate-mapping>
Schem aExport で生成したテーブルスキーマです。
253
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
create table employers (
id BIGINT not null,
name VARCHAR(255),
primary key (id)
)
create table employment_periods (
id BIGINT not null,
hourly_rate NUMERIC(12, 2),
currency VARCHAR(12),
employee_id BIGINT not null,
employer_id BIGINT not null,
end_date TIMESTAMP,
start_date TIMESTAMP,
primary key (id)
)
create table employees (
id BIGINT not null,
firstName VARCHAR(255),
initial CHAR(1),
lastName VARCHAR(255),
taxfileNumber VARCHAR(255),
primary key (id)
)
alter table employment_periods
add constraint employment_periodsFK0 foreign key (employer_id) references
employers
alter table employment_periods
add constraint employment_periodsFK1 foreign key (employee_id) references
employees
create sequence employee_id_seq
create sequence employment_id_seq
create sequence employer_id_seq
23.2. 作 者 /作 品
Work 、 Author そして Person の関係を表す以下のモデルを考えてみてください。 Work と Author
の関係を多対多関連で表しています。 Author と Person の関係は一対一関連として表しています。他
には Author が Person を拡張するという方法もあります。
以下のマッピングドキュメントはこのような関係を正確に表現しています:
254
第23章 例: いろいろなマッピング
<hibernate-mapping>
<class name="Work" table="works" discriminator-value="W">
<id name="id" column="id">
<generator class="native"/>
</id>
<discriminator column="type" type="character"/>
<property name="title"/>
<set name="authors" table="author_work">
<key column name="work_id"/>
<many-to-many class="Author" column name="author_id"/>
</set>
<subclass name="Book" discriminator-value="B">
<property name="text"/>
</subclass>
<subclass name="Song" discriminator-value="S">
<property name="tempo"/>
<property name="genre"/>
</subclass>
</class>
<class name="Author" table="authors">
<id name="id" column="id">
<!-- The Author must have the same identifier as the Person -->
<generator class="assigned"/>
</id>
<property name="alias"/>
<one-to-one name="person" constrained="true"/>
<set name="works" table="author_work" inverse="true">
<key column="author_id"/>
<many-to-many class="Work" column="work_id"/>
</set>
</class>
<class name="Person" table="persons">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
このマッピングには4つのテーブルがあります。 works 、 authors , persons はそれぞれ、仕事、作
者、人のデータを保持します。 author_work は作者と作品をリンクする関連テーブルです。以下は
Schem aExport で生成したテーブルスキーマです。
255
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
create table works (
id BIGINT not null generated by default as identity,
tempo FLOAT,
genre VARCHAR(255),
text INTEGER,
title VARCHAR(255),
type CHAR(1) not null,
primary key (id)
)
create table author_work (
author_id BIGINT not null,
work_id BIGINT not null,
primary key (work_id, author_id)
)
create table authors (
id BIGINT not null generated by default as identity,
alias VARCHAR(255),
primary key (id)
)
create table persons (
id BIGINT not null generated by default as identity,
name VARCHAR(255),
primary key (id)
)
alter table authors
add constraint authorsFK0 foreign key (id) references persons
alter table author_work
add constraint author_workFK0 foreign key (author_id) references authors
alter table author_work
add constraint author_workFK1 foreign key (work_id) references works
23.3. 顧 客 /注 文 /製 品
Now consider a model of the relationships between Custom er, Order and LineItem and Product.
T here is a one-to-many association between Custom er and Order, but how should we represent
Order / LineItem / Product? I've chosen to map LineItem as an association class representing
the many-to-many association between Order and Product. In Hibernate, this is called a composite
element.
マッピングドキュメント:
256
第23章 例: いろいろなマッピング
<hibernate-mapping>
<class name="Customer" table="customers">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="orders" inverse="true">
<key column="customer_id"/>
<one-to-many class="Order"/>
</set>
</class>
<class name="Order" table="orders">
<id name="id">
<generator class="native"/>
</id>
<property name="date"/>
<many-to-one name="customer" column="customer_id"/>
<list name="lineItems" table="line_items">
<key column="order_id"/>
<list-index column="line_number"/>
<composite-element class="LineItem">
<property name="quantity"/>
<many-to-one name="product" column="product_id"/>
</composite-element>
</list>
</class>
<class name="Product" table="products">
<id name="id">
<generator class="native"/>
</id>
<property name="serialNumber"/>
</class>
</hibernate-mapping>
custom ers 、 orders 、 line_item s 、 products はそれぞれ、顧客、注文、注文明細、製品の
データを保持します。 line_item s は注文と製品をリンクする関連テーブルとしても働きます。
257
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
create table customers (
id BIGINT not null generated by default as identity,
name VARCHAR(255),
primary key (id)
)
create table orders (
id BIGINT not null generated by default as identity,
customer_id BIGINT,
date TIMESTAMP,
primary key (id)
)
create table line_items (
line_number INTEGER not null,
order_id BIGINT not null,
product_id BIGINT,
quantity INTEGER,
primary key (order_id, line_number)
)
create table products (
id BIGINT not null generated by default as identity,
serialNumber VARCHAR(255),
primary key (id)
)
alter table orders
add constraint ordersFK0 foreign key (customer_id) references customers
alter table line_items
add constraint line_itemsFK0 foreign key (product_id) references products
alter table line_items
add constraint line_itemsFK1 foreign key (order_id) references orders
23.4. 種 々 雑 多 な マ ッ ピ ン グ 例
ここにある例はすべて Hibernate のテストスイートから取りました。そこには、他にもたくさんのマッピ
ングの例があります。 Hibernate ディストリビューションの test フォルダを見てください。
T ODO: ここに文章を埋める
23.4.1. "Typed" one-to-one association
258
第23章 例: いろいろなマッピング
<class name="Person">
<id name="name"/>
<one-to-one name="address"
cascade="all">
<formula>name</formula>
<formula>'HOME'</formula>
</one-to-one>
<one-to-one name="mailingAddress"
cascade="all">
<formula>name</formula>
<formula>'MAILING'</formula>
</one-to-one>
</class>
<class name="Address" batch-size="2"
check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
<composite-id>
<key-many-to-one name="person"
column="personName"/>
<key-property name="type"
column="addressType"/>
</composite-id>
<property name="street" type="text"/>
<property name="state"/>
<property name="zip"/>
</class>
23.4.2. 複合キーの例
259
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="Customer">
<id name="customerId"
length="10">
<generator class="assigned"/>
</id>
<property name="name" not-null="true" length="100"/>
<property name="address" not-null="true" length="200"/>
<list name="orders"
inverse="true"
cascade="save-update">
<key column="customerId"/>
<index column="orderNumber"/>
<one-to-many class="Order"/>
</list>
</class>
<class name="Order" table="CustomerOrder" lazy="true">
<synchronize table="LineItem"/>
<synchronize table="Product"/>
<composite-id name="id"
class="Order$Id">
<key-property name="customerId" length="10"/>
<key-property name="orderNumber"/>
</composite-id>
<property name="orderDate"
type="calendar_date"
not-null="true"/>
<property name="total">
<formula>
( select sum(li.quantity*p.price)
from LineItem li, Product p
where li.productId = p.productId
and li.customerId = customerId
and li.orderNumber = orderNumber )
</formula>
</property>
<many-to-one name="customer"
column="customerId"
insert="false"
update="false"
not-null="true"/>
<bag name="lineItems"
fetch="join"
inverse="true"
cascade="save-update">
<key>
<column name="customerId"/>
<column name="orderNumber"/>
</key>
<one-to-many class="LineItem"/>
</bag>
260
第23章 例: いろいろなマッピング
</class>
<class name="LineItem">
<composite-id name="id"
class="LineItem$Id">
<key-property name="customerId" length="10"/>
<key-property name="orderNumber"/>
<key-property name="productId" length="10"/>
</composite-id>
<property name="quantity"/>
<many-to-one name="order"
insert="false"
update="false"
not-null="true">
<column name="customerId"/>
<column name="orderNumber"/>
</many-to-one>
<many-to-one name="product"
insert="false"
update="false"
not-null="true"
column="productId"/>
</class>
<class name="Product">
<synchronize table="LineItem"/>
<id name="productId"
length="10">
<generator class="assigned"/>
</id>
<property name="description"
not-null="true"
length="200"/>
<property name="price" length="3"/>
<property name="numberAvailable"/>
<property name="numberOrdered">
<formula>
( select sum(li.quantity)
from LineItem li
where li.productId = productId )
</formula>
</property>
</class>
23.4.3. 複合キー属性を共有する多対多
261
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="User" table="`User`">
<composite-id>
<key-property name="name"/>
<key-property name="org"/>
</composite-id>
<set name="groups" table="UserGroup">
<key>
<column name="userName"/>
<column name="org"/>
</key>
<many-to-many class="Group">
<column name="groupName"/>
<formula>org</formula>
</many-to-many>
</set>
</class>
<class name="Group" table="`Group`">
<composite-id>
<key-property name="name"/>
<key-property name="org"/>
</composite-id>
<property name="description"/>
<set name="users" table="UserGroup" inverse="true">
<key>
<column name="groupName"/>
<column name="org"/>
</key>
<many-to-many class="User">
<column name="userName"/>
<formula>org</formula>
</many-to-many>
</set>
</class>
23.4.4. discrimination に基づく内容
262
第23章 例: いろいろなマッピング
<class name="Person"
discriminator-value="P">
<id name="id"
column="person_id"
unsaved-value="0">
<generator class="native"/>
</id>
<discriminator
type="character">
<formula>
case
when title is not null then 'E'
when salesperson is not null then 'C'
else 'P'
end
</formula>
</discriminator>
<property name="name"
not-null="true"
length="80"/>
<property name="sex"
not-null="true"
update="false"/>
<component name="address">
<property name="address"/>
<property name="zip"/>
<property name="country"/>
</component>
<subclass name="Employee"
discriminator-value="E">
<property name="title"
length="20"/>
<property name="salary"/>
<many-to-one name="manager"/>
</subclass>
<subclass name="Customer"
discriminator-value="C">
<property name="comments"/>
<many-to-one name="salesperson"/>
</subclass>
</class>
23.4.5. 代替キーの関連
263
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
<class name="Person">
<id name="id">
<generator class="hilo"/>
</id>
<property name="name" length="100"/>
<one-to-one name="address"
property-ref="person"
cascade="all"
fetch="join"/>
<set name="accounts"
inverse="true">
<key column="userId"
property-ref="userId"/>
<one-to-many class="Account"/>
</set>
<property name="userId" length="8"/>
</class>
<class name="Address">
<id name="id">
<generator class="hilo"/>
</id>
<property name="address" length="300"/>
<property name="zip" length="5"/>
<property name="country" length="25"/>
<many-to-one name="person" unique="true" not-null="true"/>
</class>
<class name="Account">
<id name="accountId" length="32">
<generator class="uuid"/>
</id>
<many-to-one name="user"
column="userId"
property-ref="userId"/>
<property name="type" not-null="true"/>
</class>
264
第24章 ベストプラクティス
第 24章 ベストプラクティス
Write fine-grained classes and map them using <com ponent>.
street (通り)、 suburb (都市)、 state (州)、 postcode (郵便番号)をカプセル化
する Address (住所)クラスを使いましょう。そうすればコードが再利用しやすくなり、リ
ファクタリングも簡単になります。
永続クラスには識別子プロパティを定義しましょう。
Hibernate makes identifier properties optional. T here are all sorts of reasons why you should
use them. We recommend that identifiers be 'synthetic' (generated, with no business meaning).
自然キーを見つけましょう。
Identify natural keys for all entities, and map them using <natural-id>. Implement equals()
and hashCode() to compare the properties that make up the natural key.
クラスのマッピングはそれぞれのクラス専用のファイルに書きましょう。
Don't use a single monolithic mapping document. Map com .eg.Foo in the file
com /eg/Foo.hbm .xm l. T his makes particularly good sense in a team environment.
リソースとしてマッピングをロードしましょう。
マッピングを、それらがマッピングするクラスと一緒に配置しましょう。
クエリ文字列を外部に置くことを考えましょう
クエリが ANSI 標準でない SQL 関数を呼んでいるなら、これはよいプラクティスです。クエリ文
字列をマッピングファイルへ外出しすればアプリケーションがポータブルになります。
バインド変数を使いましょう。
As in JDBC, always replace non-constant values by "?". Never use string manipulation to bind a
non-constant value in a query! Even better, consider using named parameters in queries.
Don't manage your own JDBC connections.
Hibernate lets the application manage JDBC connections. T his approach should be considered
a last-resort. If you can't use the built-in connections providers, consider providing your own
implementation of org.hibernate.connection.ConnectionProvider.
カスタム型の使用を考えましょう。
Suppose you have a Java type, say from some library, that needs to be persisted but doesn't
provide the accessors needed to map it as a component. You should consider implementing
org.hibernate.UserT ype. T his approach frees the application code from implementing
transformations to / from a Hibernate type.
ボトルネックを解消するには JDBC をハンドコードしましょう。
In performance-critical areas of the system, some kinds of operations might benefit from direct
JDBC. But please, wait until you know something is a bottleneck. And don't assume that direct
JDBC is necessarily faster. If you need to use direct JDBC, it might be worth opening a
Hibernate Session and using that JDBC connection. T hat way you can still use the same
265
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
transaction strategy and underlying connection provider.
Session のフラッシュを理解しましょう。
Session が永続状態をデータベースと同期させることがときどきあります。しかしこれがあまり
に頻繁に起こるようだと、パフォーマンスに影響が出てきます。自動フラッシュを無効にした
り、特定のトランザクションのクエリや操作の順番を変更することで、不必要なフラッシュを最
小限にできます。
3層アーキテクチャでは分離オブジェクトの使用を考えましょう。
サーブレット / セッション Bean アーキテクチャを使うとき、サーブレット層 / JSP 層間でセッ
ション Bean でロードした永続オブジェクトをやり取りできます。その際リクエストごとに新し
い Session を使ってください。また Session.m erge() や Session.saveOrUpdate() を
使って、オブジェクトとデータベースを同期させてください。
2層アーキテクチャでは長い永続コンテキストの使用を考えましょう。
最高のスケーラビリティを得るには、データベーストランザクションをできるだけ短くしなけれ
ばなりません。しかし長い間実行する アプリケーショントランザクション の実装が必要なこと
はしばしばです。これはユーザーの視点からは1個の作業単位(unit of work)になります。アプ
リケーショントランザクションはいくつかのクライアントのリクエスト/レスポンスサイクルにま
たがります。アプリケーショントランザクションの実装に分離オブジェクトを使うのは一般的で
す。そうでなければ、2層アーキテクチャの場合は特に適切なことですが、アプリケーショント
ランザクションのライフサイクル全体に対して単一のオープンな永続化コンテキスト(セッショ
ン)を維持してください。そして単純にリクエストの最後に JDBC コネクションから切断し、次
のリクエストの最初に再接続します。決して複数のアプリケーショントランザクションユース
ケースに渡って1個の Session を使い回さないでください。そうでなければ、古いデータで作業
することになります。
Don't treat exceptions as recoverable.
T his is more of a necessary practice than a "best" practice. When an exception occurs, roll
back the T ransaction and close the Session. If you don't, Hibernate can't guarantee that inmemory state accurately represents persistent state. As a special case of this, do not use
Session.load() to determine if an instance with the given identifier exists on the database;
use Session.get() or a query instead.
関連にはなるべく遅延フェッチを使いましょう。
Use eager fetching sparingly. Use proxies and lazy collections for most associations to classes
that are not likely to be completely held in the second-level cache. For associations to cached
classes, where there is an a extremely high probability of a cache hit, explicitly disable eager
fetching using lazy="false". When an join fetching is appropriate to a particular use case,
use a query with a left join fetch.
フェッチされていないデータに関わる問題を避けるために、 ビューの中でオープンセッション
を使う (open session in view) パターンか、統制された 組み立てフェーズ (assembly phase)
を使いましょう。
Hibernate は Data Transfer Objects (DT O) を書く退屈な作業から開発者を解放します。伝統的
な EJB アーキテクチャでは DT O は2つ目的があります: 1つ目は、エンティティ Bean がシリ
アライズされない問題への対策です。2つ目は、プレゼンテーション層に制御が戻る前に、
ビューに使われるすべてのデータがフェッチされて、 DT O に復元されるような組み立てフェー
266
第24章 ベストプラクティス
ズを暗黙的に定義します。 Hibernate では1つ目の目的が不要になります。しかしビューのレン
ダリング処理の間、永続コンテキスト(セッション)をオープンにしたままにしなければ、組み
立てフェーズはまだ必要です(分離オブジェクトの中のどのデータが利用可能かについて、プレ
ゼンテーション層と厳密な取り決めをしているビジネスメソッドを考えてみてください)。これ
は Hibernate 側の問題ではありません。トランザクション内で安全にデータアクセスするための
基本的な要件です。
Hibernate からビジネスロジックを抽象化することを考えましょう。
Hide (Hibernate) data-access code behind an interface. Combine the DAO and Thread Local
Session patterns. You can even have some classes persisted by handcoded JDBC, associated
to Hibernate via a UserT ype. (T his advice is intended for "sufficiently large" applications; it is
not appropriate for an application with five tables!)
Don't use exotic association mappings.
Good usecases for a real many-to-many associations are rare. Most of the time you need
additional information stored in the "link table". In this case, it is much better to use two one-tomany associations to an intermediate link class. In fact, we think that most associations are
one-to-many and many-to-one, you should be careful when using any other association style
and ask yourself if it is really neccessary.
なるべく双方向関連にしましょう。
単方向関連は双方向に比べて検索が難しくなります。大きなアプリケーションでは、ほとんどす
べての関連が双方向にナビゲーションできなければなりません。
267
JBoss Enterprise Application Platform 4.2 Hibernate Reference Guide
Revision History
改訂 1.0-9.4 00
2013-10-30
Landmann Rüdiger [FAMILY
Given]
2012-07-18
T owns Anthony [FAMILY
Given]
T hu Nov 19 2009
Bailey Laura [FAMILY Given]
Rebuild with publican 4.0.0
改訂 1.0-9
Rebuild for Publican 3.0
改訂 1.0-0
Initial draft.
268
Fly UP