開発者:Java
基本的なJava Persistence APIのベスト・プラクティスJava Persistence APIアプリケーションをより管理しやすくするためのシンプルなアプローチを検討し採用しましょう。 Dustin Marx著 2008年5月公開 Enterprise JavaBeans(EJB)3.0仕様の一部であるJava Persistence API(JPA)は、Java EEプラットフォームの永続化とオブジェクト・リレーショナル・マッピングに使用する標準的なAPIで、データ・バインディングに関する有効な機能を Java開発者に提供します。 1つ目は、標準とエンタープライズ両方のJava分野で永続化に使用する一般的なAPIです。 2つ目は、複数の異なるデータ・ストアに使用できる標準的なAPIです。 3つ目は、このようなさまざまなコンテキスト(標準Java、エンタープライズJava、多様なデータベース)に対して同じ方法で適用できるオブジェク ト・リレーショナル・マッピング・テクノロジーです。 ここでは、Java Persistence APIのこうした利点を十分に活用する方法についての概要を説明します。 推奨方法の共通テーマここで紹介する推奨アプローチの大部分に共通するテーマは次のとおりです。
以上の共通テーマを念頭に置きながら、JPAベースのアプリケーションを有効活用するための推奨プラクティスについて見ていきましょ う。 例外よりも規約を重視理想としては、デフォルト構成は望みどおりの構成であるのが一番です。 "Configuration by Exception"(例外による構成)をおこなえば、例外の設定が不要になります。 前提となる構成からの逸脱の頻度と程度を最小限に抑えることで、こうした理想に近い環境に近づけることができます。 デフォルト構成に例外を指定すること自体は間違っているわけではないのですが、デフォルト構成の例外を記述するメタデータの表記と管理に手間がかかること は事実です。 メタデータ構成の戦略を慎重に検討して選択多くの組織では、開発時にコード内にアノテーションを記述するのがもっとも理にかなっています。これは、コード内に構成するほうが開発 者にとっては非常に便利だからです。 こうした組織の中には、導入時と本番稼働時に外部XMLファイルを使用したほうがいい場合もあります。これはとくに、導入チームと開発チームが異なる組織 において言えることです。 JPAでは、XMLベースの構成データをアノテーションの代わりとして使用できますが、アノテーションをオーバーライドするXML構成 を使用したほうがより効果的です。 オーバーライドを使用すれば、ソース・コード開発時にはアノテーションを利用し、本番稼働時にはコード内のアノテーションをコード外でオーバーライドする ことができます。 Oracle Technology Network(OTN)の記事『 Better JPA, Better JAXB, and Better Annotations Processing with Java SE 6』で 詳しく説明しましたが、Java SE 6には、コード内のアノテーションからマッピングXMLファイルを作成する際に使用できるアノテーション処理が組み込まれています。 この方法は、開発スタッフがコード内のアノテーションを使用し、導入スタッフが外部構成を使用することが双方のメリットとなる組織に適しています。 さまざまなソフトウェアを導入するために頻繁な変更が予想される構成では、外部構成がもっとも有用です。 それに対し、複数の導入環境にまたがっているきわめて静的な構成では、コード内のアノテーションをいったん定義してしまえば頻繁に変更する必要はありませ ん。 コード内のアノテーションではなく、XML構成ファイル内での記述が必要な構成もあります。 そのような構成の例としては、永続化ユニット内の全エンティティに対するデフォルト・エンティティ・リスナーの定義が挙げられます。 そのほかに、コード内のアノテーションよりも外部構成を使用するほうがよい状況としては、ベンダー固有の設定があります。 外部構成ファイルに実装別の設定を記述しておくことで、移植可能ですっきりとしたコードを維持できます。 通常、JPAのベンダー固有のプロパティは、ソース・コード内ではなく、persistence.xmlファイル内の名前/値のプロパティで宣言する必要 があります。 データベース固有のSQL文についても、ソース・コード外のXMLディスクリプタ・ファイル内に記述することができます。 データベース固有のSQL文を使用する必要がある場合は、特定のエンティティのJavaソース・コード・ファイル内ではなく、一般的な永続化ユニットの XMLファイル内に、ネイティブの名前付き問合せとして指定してアノテーションを付けるのが最適の方法です。 JPA 1.0仕様の共同リーダーを務めるMike Keithが、OTNコラム『To Annotate or Not』の中で、XMLメタデータ戦略(XML戦略)とソース内メタデータ戦略(アノテーション戦略)との間のさまざまなトレードオフについて説明してい ます。 プロパティではなくフィールドにアクセス私自身はというと、いくつかの理由から、get/setメソッド(プロパティ)にアノテーションを付けるのではなく、エンティティの フィールドに直接アノテーションを付けてオブジェクト・リレーショナル・マッピングを指定しています。 プロパティではなくフィールドで永続化を指定するほうが断然好きだということにこれといった理由はないのですが、フィールドで永続化を指定する場合のさま ざまなメリットを合わせると、そのほうが魅力的に感じます。 永続化は、データの保存、更新、検索のすべてにかかわるものであるため、get/setメソッドで間接的に表記するよりも、データに直 接表記したほうが明確です。 永続化指定時に、getterはマークしてsetterはマークしないことに注意する必要もありません。 それに、get()メソッドを一時的なものとしてマークするよりも、フィールドを一時的なものとしてマークして永続化されないことを示したほうがすっきり しています。 プロパティではなくフィールドを使用すれば、get/setメソッドが基本となるフィールドに関連するJavaBeansのネーミング規則にしたがってい るかどうかを確認する必要もありません。 クラスのデータ・メンバーを見て、各メンバーの名前、データ型、関連コメント、永続化情報を1カ所で決定できるほうが便利です。 get/setメソッドのビジネス・ロジックと永続化が実行される順序は保証されていません。 ビジネス・ロジックをget/setメソッドから切り離すこともできますが、フィールドにアノテーションを付ければ、get/setメソッドにあとでビジ ネス・ロジックを追加しても問題ありません。 1度に複数の属性を操作または取得するメソッドや、名前に"get"や"set"が付かないメソッドを使いたい場合もあるでしょう。 フィールド・アノテーションを使用すれば、こうしたメソッドを自由に記述して好きな名前をつけることができ、永続化とは直接関係のない @Transientアノテーションや"transient"キーワードをメソッドの前につける必要もありません。 @EmbeddedIdで複合キーを指定私は@EmbeddedIdを使用して複合キーを指定していますが、それには次の3つの理由があります。 1. 主キー以外の組込みJavaクラスの@Embeddedアノテーションと一貫性をもたせることができるため。 2. @Idアノテーションを使用してエンティティ内の複数のデータ・メンバーにアノテーションをつけるのではなく、複合キーをエンティティ内で1つのキーとし て表現できるため。 3. 主キー列の@Columnなどの列マッピングを1つのJavaクラスにカプセル化できるため。 これは、複合キーの各列に対し、オブジェクト・リレーショナル・マッピングの詳細をエンティティに強制実行させるよりもよい方法です。 つまり、なぜ複合主キーに@EmbeddedIdを使用するのかというと、主キーの詳細を1つの@Embeddableクラスにグルー プ化できるからです。 個々の要素をエンティティ内に配置するのではなく、主キークラスとして1つの単位にまとめておくことができるので、アクセスが簡単になります。 理想としては単一値のキーを使用するのが一番ですが、これはJPA開発者にとってもっとも手間がかからない方法だからです。 主キーに真数型のみを使用JPA仕様では、概数型、中でもとくに浮動小数型(Float型とDouble型)を使用しないように警告しています。 私の場合、JPAの主キーにはできるだけ整数型のサロゲート・キーを使うようにしています。 標準JPAコードの実現に向けて下記の移植可能なJPAコードを維持する一般ガイドラインは、JPA仕様の任意または未定義の機能に関する警告事項に基づいています。 参照を使用してテーブルを主キーに関連づけるJPA仕様では、あるテーブルの列が別のテーブルの非主キー列を参照する実装が許可されていますが、そのためにJPA実装を使う必要は ありません。 したがって、異なるJPA実装間で移植可能なアプリケーションの場合は、参照を使用してテーブルを別のテーブルの主キー列に関連づけるのがベストです。 私は原則的にこの方法をデータベースで使用しています。 移植可能な継承マッピングを使用JPAプロバイダが"具象クラスごとのテーブル"をオプションとして実装している場合でも、JPAプロバイダの移植性が必要な場合には これを使用しないほうがよいでしょう。 さらに、1つのJavaエンティティ・クラス階層につき1つの継承マッピングを使用することを推奨します。これは、1つのクラス階層に 混在する複数の継承マッピングのサポートは、JPA実装には必要ないためです。 JPA実装の移植性に関するそのほかの注意点これまでに述べた点以外にも、JPA仕様では、移植可能なJPAベースのアプリケーションを開発する上での注意点が挙げられています。 一般に、任意であるか未定義であるか、あるいは曖昧に書かれているか特定されているかに関係なく、JPA実装間で移植性がないと仕様に書かれている場合 は、絶対に必要でない限り使用するべきではありません。 たいていの場合、移植性のないものを使用せずに済ますことはさほど困難ではありません (移植可能なJPAアプリケーションに関連した推奨されるリソースとしては、2007 JavaOne Conference のプレゼンテーション『Java Persistence API:Portability Do’s and Don’ts』や、『Portable Persistence Using the EJB 3.0 Java Persistence API』という記事があります。 これらはいずれも一番下にある"そのほかのリソース"で紹介しています)。 実装固有の機能を有効活用JPA実装には、非常に便利な非標準機能("拡張機能")が備わっている場合があります。 多様な標準実装間での移行性を高めるには、できる限りアプリケーションを標準ベースにしたほうがよいのですが、実装固有の機能を一切使 用してはいけないというわけではありません。 ただし、すべてを標準機能で済ます場合と、ベンダー固有の機能を採用する場合とで、コストとメリットを比較してみる必要があります。 JPA実装固有の機能と拡張機能を使用するかどうかを決定する場合は、次の点を考慮する必要があります。
上記の項目を検討したうえでトレードオフを決定するのは、たとえば、JPAベースのアプリケーションでOracle TopLink Essentialsのログ機能を使用する場合です。 私自身はこのプロバイダ固有の機能に満足していますが、それは次の理由によります。
そのほかの非常に便利なプロバイダ固有の機能としては、第2レベルのキャッシュがあります。これは許容範囲のパフォーマンスを実現する 上で往々にして不可欠な機能です。 リファレンス実装の拡張機能については、『TopLink Essentials JPA Extensions Reference』("そのほかのリソース"を参照)で確認できます。 標準SQLとデータベースの使用に向けてデータベース固有の機能を使用する場合は、JPAプロバイダ固有の機能を使用する場合よりも大きなリスクを伴います。これは、データ ベース固有の機能がJPAプロバイダ固有の機能のようにはすんなりと処理されないからです。 標準JPAの問合せ文を使用して、データベースの独立性を維持します。 コード内のデータベース固有のSQL文に赤いフラグが表示された場合は、@NamedNativeQueryおよび @NamedNativeQueriesアノテーション(または対応するXMLディスクリプタの要素)が使用されています。 同様に、EntityManager.createNativeQueryメソッドの呼出しも、データベースの独立性を示します。 データベース固有の機能を有効活用ベンダー固有の機能を使用することが保証されている場合でも、ベンダー固有の機能を標準化されたデータベース・アクセスと明確に区別し て分離させることができます。 私の場合、データベース固有の問合せをXMLデプロイメント・ディスクリプタ(1つ以上のネイティブの名前付き問合せ要素)に配置し て、アノテーションを含む実際のコードをできる限りベンダー固有のコードとは分けるようにしています。 そうすれば、専用データベースのコードを標準指向のコードと混ぜるのではなく、外部のディスクリプタに分離させることができます。 また、ネイティブの名前付き問合せのXML要素を、エンティティのサブ要素としてではなく、オブジェクト・リレーショナル・マッピング・ファイルのルート 要素のサブ要素として記述しています。 ネイティブの名前付き問合せは、その中の1つが特定のエンティティのJavaクラスで定義されている場合でも永続化ユニット全体を対象 とするため、ネイティブの名前付き問合せの名前に一意の識別子をつけておくことが推奨されます。 ネイティブの名前付き問合せをまとめて外部XMLマッピング・ファイルのルート要素の下に配置しておけば、名前の衝突を検出しやすくなります。 この方法のデメリットは、問合せで返されたエンティティを見つけにくいことです。ただし、返されたエンティティ名をネイティブの名前付き問合せの一部とし て含めるようにすれば、この問題を解決できます。 Java SE環境とJava EE環境の両方で使用できるJPAコードの設計標準JavaとエンタープライズJavaの両方で機能する共通のAPIを使用することのメリットは、標準アプリケーションとエンタープ ライズ・アプリケーションの両方で機能するJavaベースの永続レイヤーを記述できることです。 効果的な階層化をおこなうことにより、JPAベースのデータベース・アクセス(DAO)コードを標準Javaとエンタープライズ Javaのコンテキストで使用できるようになります。 ただし、それには、エンティティ・クラスとDAOレイヤーでトランザクションの処理をおこなわないことが必要です。そうしないと、エンタープライズ Javaのアプリケーション・サーバーのトランザクションと競合してしまう可能性があります。 トランザクションは、エンティティ・クラスから標準Java環境のクライアントに送られるようにする必要があります。 次の図は、標準Java環境とエンタープライズJava環境の両方で共通のエンティティ・クラスを使用する方法を示しています。 このようなエンティティ構造にすれば、わずかな労力で再利用性の高いエンティティ・クラスが構築できます。 エンティティ・マネージャのアクセスをホスト環境固有のレイヤーに戻し、トランザクションの処理を同じレイヤーに戻せば、JPAエン ティティ・クラスとデータベース・アクセス・レイヤーを、標準Java環境、Java EE Webコンテナ、Java EE EJBアプリケーション・サーバーで再利用できるようになります。 上の図では、JPAトランザクションとエンティティ・マネージャをWeb層で処理する設定となっていますが、私の場合はこれをWeb層 からアクセス可能なステートレスSession Beanで処理するように設定しています。 JPAをWeb層に配置しているのは、トランザクションとエンティティ・マネージャの処理を共通のJPAおよびDAOコードと切り離せることを示すためで す。 異なる種類のエンティティ・マネージャ(アプリケーション管理とコンテナ管理)を同時に、あるいは置き換えて使用することはできません ので注意してください。 また、1つのエンティティ・マネージャを複数の同時トランザクションで使用することもできません。 JPAコードをアプリケーション・レイヤーで使用Java Persistence APIはデータベース・アクセスを目的としているため、通常はビジネス層以外のアプリケーション層では使用しないでください。 JPAコードをプレゼンテーション・レイヤーに配置すると、プレゼンテーション・レイヤーの外部にあるJavaベースのアプリケーションからは再利用でき なくなり、JPAの主要なメリットが損なわれます。 メタデータやコメントよりも自己記述的なコードを使用コメントやアノテーションはコードの用途や目的を記述するのには便利ですが、コードが自己記述的であればなおよいでしょう。 "transient"キーワードは、Javaプログラミング言語に長年組み込まれており、標準Javaコードでフィールドが永続化さ れないことを示すものです。 私は、@TransientアノテーションやXMLエントリではなくこのキーワードを使用して、永続化の例外を表記しています。 ただし、エンティティ・クラスにシリアライズ可能で永続化可能でないフィールドが必要な場合は別です。 その場合に使用できるのは、@Transientアノテーション(または相当するXML)のみです。 ネーミング規則を使用して読みやすいJPAコードを記述ネーミング規則を使用して、ほかの開発者が記述したコードを読みやすく管理しやすいコードに改良することができます。 JPA関連のネーミング規則は、JPA関連のデフォルトを補完するものです。 ネーミング規則を使用すれば、名前付き問合せのそれぞれに一意のラベルを付けることができるため、永続性コンテキスト内のすべての名前 付き問合せに一意の名前を設定できます。 これをもっとも簡単におこなうには、エンティティにもっとも密接に関連する名前付き問合せの名前に、エンティティ・クラス名の接頭辞をつけます。 JPAブループリントでは、名前付き問合せとJPAベースのコードのそのほかのアスペクトに、同様のネーミング規則を使用することを推奨しています。 J2SE SE 5以降の機能を活用JPAベースのアプリケーションで使用するのはJ2SE 5以降となるため、コードでJ2SE 5の機能を使用できます。 targetEntityを使用しなくても、標準でエンティティ間に1対多や多対多の関係を標準で指定できます。 そのため、自己記述的なコードが記述でき、ソース内のアノテーションや外部のXML構成を使用する必要がありません。 列挙型はエンティティとしては使用できませんが、永続エンティティのデータ・メンバーとして使用して、データ・メンバーの値の有限範囲 について、型安全の確保と制御をおこなうことができます。 パフォーマンスの問題ここまでは管理しやすいJPAベースのアプリケーションの開発を重視してきましたが、JPA仕様では、JPAコードの移植性を損なわず にパフォーマンスを調整する便利な"フック"が多数提供されています。 JPAプロバイダは、認識されないプロパティ名をもつpersistence.xmlファイル内のプロバイダ・プロパティを無視します。 問合せヒントは、JPAプロバイダが適用していない場合は無視されますが、それでも私は問合せヒントをJavaコードではなく外部のXMLファイルに配置 しています。 このようなJPAプロバイダの"フック"は、パフォーマンス向上のために必要に応じて使用するべきですが、私の場合は、コードを分離し てできる限り外部のXMLディスクリプタ・ファイルで宣言するようにしています。 最新ツールの活用主要な統合開発環境(IDE)には、現在さまざまなJPA関連ツールがバンドルされています。 Oracle JDeveloperには、指定されたデータベース・テーブルから適切なアノテーションを直接使用して、JPAベースのエンティティ・クラスを簡単に作成 できるウィザードが備わっています。 Oracle JDeveloperのユーザーは、数回クリックするだけで、新しく作成されたEntity Beanのファサードとして機能するステートレスSession Beanを作成できます。 NetBeans 6.0にも同様のJPAウィザードが備わっており、Eclipse DaliプロジェクトはEclipse IDEのJPAツールをサポートしています。 今後も便利なJPA関連ツールがさらに提供されるでしょう。 JPAにSpringを追加Springを使用すれば、ソース・コードを変更することなく、標準Java環境、Webコンテナ、アプリケーション・サーバー全体の EJBコンテナで簡単に実行できるJPAベースのアプリケーションを記述できます。 これは、Springコンテナに備わっているコード外に構成されたデータソースの注入機能と、同じくコード外に構成されたアスペクト指向プログラミング経 由のトランザクションをサポートする機能を使用しておこなうことができます。 Springフレームワークにより、さまざまな環境(Java SEスタンドアロン、Java EE Webコンテナ、Java EE EJBコンテナ)に固有のJPA処理を外部構成ファイルに分離して、JPAベースのコードの透過性を保つことができます。 Spring 2.0のもう1つの機能としては、@Repositoryアノテーションがあります。これは、JPAのPersistenceExceptionに内在す るデータベース固有の問題を評価する場合に役立つ機能です。 さらに、Springフレームワークは、JPAプロバイダ間に共通のJPAプロバイダの拡張機能を参照する場合にも便利です。 『 Using the Java Persistence API with Spring 2.0』("そのほかのリソース"を参照)には、Springフ レームワークをJPAとともに使用する場合の詳細が記載されています。 Java EE 5とEJB 3.0のベスト・プラクティスを適用以下の効果的なEJBベスト・プラクティスを実践すれば、JPAをJava EE環境で最大活用できます。 依存性の注入などのEJB 3.0の機能を適切に使用すれば、JPAをJava EE環境でもっとも効果的に使用できます。 EJBベスト・プラクティスをJPAに適用するもう1つの例としては、ステートレスSession BeanをJava EEアプリケーションのJPAエンティティのファサードとして使用するケースがあります。 関連テクノロジーのベスト・プラクティスを習得して適用Java Persistence APIは、ほかの多くのテクノロジーと密接に関連しているため、そうしたテクノロジーのベスト・プラクティスから多くの知識を得ることができます。 Java Persistence APIの関連テクノロジーには、リレーショナル・データベース、SQL、オブジェクト・リレーショナル・マッピング(ORM)ツール、JDBCなどがあり ます。たとえば、名前付きパラメータや位置パラメータをPreparedStatementとともに使用するJDBCのベスト・プラクティスは、JPAに 反映されています。 JPAは、セキュリティと潜在的なパフォーマンスを向上するため、JPA問合せ言語で名前付きパラメータと位置パラメータをサポートしています。 JPA標準の習得Java Persistence API仕様は、非常にわかりやすく構成されています。 Java Persistence API仕様では、どのJPA機能が実装オプションであり、移植不可能であるかが説明されています。 また、将来起こり得る傾向や方向性に加え、予測される変更や追加をおこなった場合に競合が懸念される行為への警告についても述べられています。 Mike Keithは、"機能の使用方法を正しいコンテキストで抽出して適切に説明すること"は仕様の必須事項ではないと指摘しています。 したがって、JPA標準をより効率的に理解するには、JPAを正しく適切に使用する方法について明確に説明した参考資料を入手する必要があります。 そうしたリソースの一部を"そのほかのリソース"で紹介しています。 JPAブループリントの習得Java Persistence APIブループリントの ドキュメン トにも、JPAを有効活用するための情報が掲載されています。 JPAに関するOracleリソースの活用オラクルは、Java Persistence APIの開発で主要な役割を果たしています。オラクルは、これまでJPA 1.0エキスパート・グループの共同リーダーとして、JPAリファレンス実装に密接に関わってきました。 また、JPA 1.0リファレンス実装(Oracle TopLink Essentials)の提供に加え、JPA 2.0リファレンス実装を実現するためのEclipseLinkプロジェクトを率いています。 さらに、Oracle TopLink 11gを、リファレンス実装以上の機能を備えたJPA実装として提供しています。 OTNでも、 多 数のJPAリソースが提供されています。 その中の推奨されるリソースは、『 JPA Annotation Reference』と、OTN技術記事の『 Taking JPA for a Test Drive』です。 他人の経験から学ぶ(JPA関連ブログ)JPAの使用実績が積み重なるにつれ、今後もさらに多くの複雑なJPAのベスト・プラクティスや技法が紹介されることでしょう。 中でもタイムリーで関連性が高い情報を得ることができるのは、オンライン・リソースです。 ブログはよい部分も悪い部分もありますが、優れたJPA指向のブログからは、JPA開発に欠かせないヒントや知識を得ることができます。 私にも、JPA作業をおこなう際に役立てているJPA関連ブログがいくつかあります。 それらのブログの一部を"そのほかのリソース"で紹介しています。 結論Java Persistence APIを使用すれば、Java SEとJava EEの開発者は、単一の標準化されたデータベース永続化メカニズムを構築できます。 ここで紹介した方法を実践すれば、JPA仕様のメリットを実現するJPAベースのコードを開発できます。 そのほかのリソースEclipse Dali プロジェクト
EclipseLink
Java Persistence API ブループリント
『Java Persistence API:Best Practices and Tips』(2007 JavaOne Conference)
『Java Persistence API:Portability Do’s and Don’ts』(2007 JavaOne Conference)
『Java Persistence 2.0』(2007 JavaOne Conference)
『JPA Annotation Reference』
Michael Bouschenのブログ
『Portable Persistence Using the EJB 3.0 Java Persistence API』
『Pro EJB 3:Java Persistence API』(Apress、2006年、ISBN 978-1590596456)
Sahooのブログ
『TopLink Essentials JPA Extensions Reference』
『Portable Persistence Using the EJB 3.0 Java Persistence API』
Wonseok Kimのブログ
Dustin Marxは、Raytheon CompanyのSenior Software Engineerです。 |
||||||||||||||||||||||||||||||||||||||||||||||
|