Red Hat Training
A Red Hat training course is available for Red Hat JBoss Web Server
第25章 ベストプラクティス
- クラスは細かい粒度で書き
<component>
を使用してマッピングしましょう。 street
(通り)、suburb
(都市)、state
(州)、postcode
(郵便番号)をカプセル化するAddress
(住所)クラスを使いましょう。そうすればコードが再利用しやすくなり、リファクタリングも簡単になります。- 永続クラスには識別子プロパティを宣言します:
- Hibernate では識別子プロパティはオプションですが、使用すべき理由がたくさんあります。識別子は「人工的」つまり、業務的な意味を持たない状態で生成されたものにすることをおすすめします。
- 自然キーを特定します:
- すべてのエンティティに対して自然キーを見つけて、
<natural-id>
でマッピングしましょう。自然キーを構成するプロパティを比較するために、equals()
とhashCode()
を実装しましょう。 - クラスのマッピングはそれぞれのクラス専用のファイルに書きましょう:
- 単一の巨大なマッピングドキュメントを使用しないでください。
com.eg.Foo
クラスならcom/eg/Foo.hbm.xml
ファイルにマッピングしましょう。このことは、特にチームでの開発に意味があります。 - リソースとしてマッピングをロードします:
- マッピングを、それらがマッピングするクラスと一緒に配置しましょう。
- クエリ文字列を外部に置くことを考えます:
- クエリが ANSI 標準でない SQL 関数を呼び出す場合にこれは推奨されます。クエリ文字列をマッピングファイルへ外部化することでアプリケーションがよりポータブルになります。
- バインド変数を使いましょう。
- JDBC の場合と同じように、定数でない値は必ず "?" で置き換えましょう。クエリで定数でない値をバインドするために文字列操作を使ってはいけません。クエリで名前付きのパラメータを使うことも考慮すべきです。
- 自身のJDBC コネクションを管理してはいけません。
- Hibernate ではアプリケーションが JDBC コネクションを管理することができますが、これは最終手段だと思ってください。組み込みのコネクションプロバイダを使うことができなければ、
org.hibernate.connection.ConnectionProvider
を実装することを考えてください。 - カスタム型の使用を考えましょう:
- ライブラリから持ってきた Java 型を永続化する必要があるとします。しかしその型には、コンポーネントとしてマッピングするために必要なアクセサがないとします。
org.hibernate.UserType
の実装を考えるべきです。このアプローチは、アプリケーションコードがHibernate 型から/へ変換実装を行わなくてもよくなります。 - ボトルネックがある場合はハンドコード のJDBC を使用します:
- システムのパフォーマンスクリティカルな領域では、操作の種類によって JDBC を直接使うと利点がある場合もあります。しかし、JDBC は必ずしも早くなるわけではありません。何がボトルネックになっているか はっきりする まで待ってください。JDBC を直接使う必要があれば、JDBC コネクションを利用して、Hibernate Session をオープンし、
session.doWork(Work)
を利用し、作業オブジェクトとしてJDBCの操作をラップすることができます。こうすると、依然として同じトランザクション戦略と基盤となるコネクションプロバイダが使うことができます。 Session
のフラッシュを理解しましょう:- Session が永続状態をデータベースと同期させることがときどきあります。しかしこれがあまりに頻繁に起こるようであれば、パフォーマンスに影響が出てきます。自動フラッシュを無効にするか、特定のトランザクションのクエリや操作の順番を変更することで、不必要なフラッシュを最小限に抑えることができます。
- 3層アーキテクチャでは分離オブジェクトの使用を考えましょう:
- サーブレット / セッション Bean アーキテクチャを使うとき、サーブレット層 / JSP 層間でセッション Bean でロードした永続オブジェクトをやり取りできます。その際リクエストごとに新しい Session を使ってください。また
Session.merge()
やSession.saveOrUpdate()
を使って、オブジェクトとデータベースを同期させてください。 - 2層アーキテクチャでは長い永続コンテキストの使用を検討しましょう:
- 最高のスケーラビリティを得るには、データベーストランザクションをできるだけ短くしなければなりません。しかし 長い間作動するアプリケーショントランザクション の実装が必要なことはしばしばです。これはユーザーの視点からは1個の作業単位(unit of work)になります。アプリケーショントランザクションはいくつかのクライアントのリクエスト/レスポンスサイクルにわたることもあります。アプリケーショントランザクションの実装に分離オブジェクトを使うのは一般的です。二層アーキテクチャでの適切な方法は他にもあり、その方法とはアプリケーショントランザクションのライフサイクル全体で1つの永続コンタクトセッションをオープンし、それを保持することです。その後、JDBC コネクションを各リクエストの最後で切断し、次のリクエストの始めに再接続するだけです。1つのセッションを複数のアプリケーショントランザクションで共有してはいけません。共有してしまうと、古いデータで作業することになります。
- 例外を復帰可能なものとして扱ってはいけません:
- これは「ベスト」プラクティスというよりは、必須のプラクティスです。例外が発生したときは
Transaction
をロールバックして、Session
をクローズしてください。そうしないと Hibernate はインメモリの状態が永続状態を正確に表現しているかの保証ができません。例えば、 この識別子を持つインスタンスがデータベース上に存在するかを決定するにはSession.load()
を使うのではなく、Session.get()
かクエリを使ってください。 - 関連にはなるべく遅延フェッチを使いましょう:
- 即時フェッチは控えめにしましょう。二次キャッシュには完全に保持される可能性の低いクラスの関連の大半には、プロキシと遅延コレクションを使ってください。キャッシュされるクラスの関連、つまりキャッシュがヒットする可能性が非常に高い関連は、
lazy="false"
で積極的なフェッチを明示的に無効にしてください。結合フェッチが適切な特定のユースケースには、クエリでleft join fetch
を使ってください。 - フェッチされていないデータに関わる問題を避けるために、ビューの中でオープンセッション (open session in view) パターンか、統制された 組み立てフェーズ (assembly phase) を使いましょう:
- Hibernate では、開発者は単調なData Transfer Objects (DTO) を記述をしなくてもよくなっています。従来の EJB アーキテクチャでは DTO は2つ目的があります: 1つ目は、エンティティ Bean がシリアライズされない問題への対策です。2つ目は、プレゼンテーション層に制御が戻る前に、ビューに使われるすべてのデータがフェッチされて、 DTO に復元されるような組み立てフェーズを暗黙的に定義します。Hibernate では1つ目の目的が不要になります。ビューのレンダリング処理全体で、永続コンテキスト(セッション)をオープンにする準備ができていなければ、組み立てフェーズはまだ必要です。分離オブジェクトのどのデータが利用可能かについて、プレゼンテーション層と厳密な取り決めをしているビジネスメソッドを考えてみてください。これは Hibernate 側の問題ではなく、トランザクション内で安全にデータアクセスするための基本的な要件です。
- Hibernate からビジネスロジックを抽象化することを考えましょう:
- Hibernate のデーデタアクセスコードをインターフェースで見えなくします。 DAO と Thread Local Session パターンを組み合わせましょう。
UserType
で Hibernate に関連付けると、ハンドコードした JDBC で永続化するクラスを持つこともできます。しかし、このアドバイスは「十分大きな」アプリケーションに対してのもので、テーブルが5個しかないようなアプリケーションには当てはまりません。 - 珍しい関連マッピングは使わないようにしましょう:
- 実際のテストケースに本当の多対多関連があることは稀です。ほとんどの場合「リンクテーブル」の付加情報が必要になります。この場合、リンククラスに2つの一対多関連を使う方がはるかによく、実際、多くの関連は一対多と多対一であるため、他のスタイルの関連を使うときは本当に必要かどうかを考えてみてください。
- 双方向関連のほうが望ましいです:
- 単方向関連は双方向に比べて検索が難しくなります。大きなアプリケーションでは、ほとんどすべての関連が双方向にナビゲーションできなければなりません。