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