はじめての Java Persistence API

2011年12月19日 at 2:03 午後 2件のコメント

今日は、Java Advent カレンダーの一貫で 12/19 分のエントリを記載します。昨日のなぎせさんに続き掲載します。また明日は私の盟友である岡崎さんです。

先日、なぎせさんより、「ProxyパターンとProxyクラスと黒魔術」と題してSQL の事を取り上げていただきました。また私自身、先日岡山のイベントで JPA について登壇したばかりなので、今日は DB つながりということで JPA (Java Persistence API) について岡山の資料を利用しながらご紹介します。

岡山の発表資料の全部はこちらです。

Java でデータベース (DB) 接続をする際、Java で一番最初に DB 接続をサポートした事から、ながく JDBC が利用されてきました。その後、J2EE に EJB の CMP Entity Bean が導入されましたが、これはとても重量級で扱いにくいフレームワークだったため、残念ながらあまり浸透しませんでした。それに変わって登場したのがオープンソースのフレームワークである JDO/Hibernate などです。

昨今、「もう Java EE 6 でいいんじゃない」、Java EE 6 はかんたんになりましたよ、などのブログ・エントリを書いたり、いろいろな所で登壇させて頂きましたが、JPA もまたかんたんに利用できるようになった、 Java EE の標準 O/R マッピング技術です。JPA は上記のようなオープンソースの O/R マッピング・フレームワークの良い部分を数多く取り入れ、かなり扱いやすくなっていますので、是非今一度標準の JPA のご使用を検討ください。また、JPA 2.0 は JPA 1.0 に比べ表現力がかなり豊かになっていますので、JPA 1.0 で物足りなさを感じられた方も今一度ご検討ください。JPA 2.0 になり、通常のビジネスアプリケーション開発のニーズをほぼ満たせると考えております。

また、ご存知の方もいらっしゃるかと思いますが、JPA は Java EE コンテナ以外の、通常の Java アプリケーションからも利用することができるようになっています。実際、JPA は GAE などでも利用できます。つまり今まで JPA はアプリケーションサーバ上でしか利用できないと思われていた方も、Java EE 環境がなくても JPA は利用できますので Java アプリケーション開発等コンテナの無い環境でも使用をご検討ください。

それでは、JDBC に比べて何が便利になっているかについて説明します。JDBC は低レベルの API しか提供していなかったため、自分で考慮、実装しなければならない事が数多くありました。例えば、JDBC をそのまま利用した場合、コネクションプールの実装はデフォルトでないため、DB にたいする問い合わせ(クエリ)を行う際に、毎回 DBに対してコネクションを張りにいかなければなりませんでした。またキャッシュも実装されていないため、毎回 DB にたいする操作を行わなければなりません。DB サーバにたいする問い合わせ(クエリ)数が少ない場合は、コネクションプールも、キャッシュもさほど重要ではないかもしれませんが、Web アプリケーションのように、クライアントからのリクエスト毎に DB の情報を参照するようなアプリケーションの場合、リクエスト毎に接続を張る、もしくはたとえ同じ情報を読むだけでも DB に接続するなど、必要以上に DB サーバに対して高負荷が掛かってしまいます。このような場合、コネクションプールを利用して DB 接続を再利用したり、キャッシュを利用する事で DB サーバにたいする負荷を軽減できます。さらに、JDBC を利用した場合トランザクション管理についても全て自身で実装、管理しなければならないため、JDBC を利用した場合、数多くの事を考慮しなければならない事がわかります。
JPA を利用すると、このような JDBC で開発者が考慮し実装しなければならない多くの機能を JPA のフレームワーク自身で既に実装していますので、開発者はそれらを再利用、もしくはコンテナに任せることがで、より安全にスケールするアプリケーションをコード量少なく短時間で開発できるようになります。つまり一言でいうならば、JPA を使用する場合、開発生産性が大幅に向上するという事です。たとえば、統合開発環境と連携する事で基本的な CRUD 操作のひな形まで自動生成してくれるためコード記述量も格段に減ります。

それでは実際に、 JPA の構成要素についてご紹介します。JPA は persistence.xml という XML 設定ファイル中に PersistenceUnit を(1つ以上)定義します。この PersistenceUnit 名は EntityManagerFactory のインスタンスを生成するために必要です。EntityManagerFactory から EntityManager のインスタンスを生成し、DB にたいする操作をおこないます。

永続化:EntityManager#persist()
削除:EntityManager#remove()
検索:EntityManager#find()

DB の操作を行う際、DB テーブルにたいして Java オブジェクトにマッピングした、Entity クラス(@Entity アノテーションを付加したクラス)を利用しておこないます。Java はオブジェクト指向型言語でクラスとして表現します、一方 DB はテーブルとして表現し、個々のデータを行(ロー)で表現します。JPA 2.0 は JPA 1.0 にくらべ Entity と DB テーブルのマッピング方法も数多く追加で提供されていますので、複雑な DB テーブルも Entity としてマッピング可能です。(本ページ下の参考資料をご参照)


繰り返しますが、Entityは @Entity のアノテーションを付加した単なる POJO です。この Entity クラスは JPA によって管理されていない状態では、単なる Java のオブジェクトとして扱います (例 new MyEntity() でインスタンスを生成した直後の状態)。Persistence Context による管理状態と管理されていない(デタッチ)状態でなにが違うかライフサイクルを用いて紹介します。管理状態とは、Persistence Context によって管理されている状態を指し、すべての Entity はPersistence Context によって管理されています。この状態の時に EntityManager を使って DB に永続化等を行う事ができます。

ライフサイクルの各状態を、実際のコードを用いて説明します。この例ではまず、Customer クラスが Entity で DB の CUSTOMER テーブルにマッピングしています。new Customer(); でインスタンスを作成します。この際、Java の Heap に Customer オブジェクトは含まれていますが、この時点では Persistence Context 内でまだ管理されていません。EntityManager#persist(customer) を実行する事により Persistence Context で管理状態となります。この状態で DB テーブルに対して操作を行うことができますが、たとえばトランザクションのコミット tx.commit() が呼び出されると、DB に対して永続化が行われ、この時に Customer の Entity は Persistence Context の管理状態から外れます(デタッチ状態)。ただし、この Persistence Context の管理状態から外れた際も、Customer は Java Heap 内に参照が残っているため、Java プログラムからは Customer オブジェクトに対しては操作する事ができます(例えば、Customer 電話番号の変更等)。管理状態から外れデタッチ状態の Customer に対して変更を加えた場合、対応する DB の行(ロー)と整合性がとれていません、そこでデタッチ状態の時に加えられた変更を EntityManager#merge() によって更新することができます。

上記までで、Entity, Entity のライフサイクル等を紹介しましたが、EntityManager の管理はアプリケーションサーバ等のコンテナ上で管理する方法と、アプリケーションで管理する方法の二種類があります。またそれぞれでできること、実装方法も異なります。下記にそれぞれにおける違いについてご紹介します。まず、コンテナ上で EntityManager を生成する方法は、@PersistenceContext のアノテーションを付加し persistence.xml に定義した PersistenceUnit 名(ここでは MYJPA_PU)を指定し、EntityManager にインジェクションします。ここでインジェクションされた EntityManager はコンテナ上で動作するため、トランザクション管理等もコンテナ側で行なってくれます。つまりトランザクション管理のコードを明示的に記載しなくても、EntityManager#persist() を実行した際に何らかの例外が発生した場合は、コンテナが自動的にロールバックします。

一方アプリケーションから管理する場合は、@PersistenceContext のアノテーションを付加する方法ではなく、Persistence.createEntityManagerFactory(“MYJPA_PU”)でファクトリを生成後、EntityManagerFactory#createEntityManager() にて EntityManager のインスタンスを生成します。アプリケーションから管理する場合は、コンテナによってトランザクション管理ができないため自身の手でトランザクション管理のコードを記述する必要があります。

最後に DB にたいする問い合わせ方法について紹介します。最もかんたんな方法として EntityManager の find(), getReferecnce() を利用して ID を指定し行う方法があります。しかし一般的には SQL に精通する開発者が問い合わせ(クエリー)を実装しやすいように、JPQL(Java Persistence Query Language) というSQL ライクなクエリ言語を使用して問い合わせをおこないます。通常の SQL はカラムを指定して問い合わせを実施しますが、JPQL では Entity を使用して問い合わせをおこないます。JPQL もまた JPA 2.0 は JPA 1.0 にくらべ表現力が高くなっており、さまざまな問い合わせを実現できますので是非 JPQL をご利用ください。

次に、DB へ問い合わせを行うために、JPQL よりもさらに型安全性を高める実装が可能な Criteria API について紹介します。Criteria API は Java プログラミングによる問い合わせが可能な API を提供します。具体的には、CriteriaBuilder, CriteriaQuery などのクラス、メソッドを使用して実装します、例えば下記のように JPQL クエリを、Java プログラミングで実装する事ができます。

ここで一番最後の行に記載する person.get(“name”) と記載している箇所に注目してください。ここでは、正しく “name” という文字列を使用して記述していますが、仮に “name” と入力すべき所を “nema” と入力ミスしてしまった場合を考えてください。このプログラムを実行するためコンパイルをおこないますが、文法上の間違いはないためコンパイルエラーは発生しません。結果として実行時にランタイムエラーが発生します。


つまり上記のように、Criteria API だけでは完全な型安全性が確保できていないことがわかります。型安全性をより高めるためには、Criteria API に加え Metamodel API を併用します。この Metamodel クラス(元の Entity に対して ”_” アンダーバーが付加)は統合開発環境によっては自動生成してくれるため、自分で作成しなくても良い場合もありますが、この Metamodel クラスを Criteria API と併用すると、person.get(“name”) の代わりに person.get(Person_.name)と記載でき、クラスのフィールドで指定可能となるため、コンパイル時に間違いを検知できるようになりランタイムエラーの発生を抑制できます。型安全をより求める場合は、Criterial API と Metamodel クラスを共にご使用ください。

最後に、繰り返しになりますが、
JDBC で実装する場合、自分で実装しなければならないコード、検討しなければならない箇所が多々ありますが、JPA を利用すると、そういった共通で必要な機能はすべてフレームワークが生成してくれています。全ての場合で JPA が有効とは申し上げませんが、JPA を使う事によりスケーラビリティを高める事が容易にできたり、また開発生産性が高まる事によって、よりビジネスロジックの開発に集中していただく事ができるようになります。また運用・保守時においても実装コードの可読性が大幅に高まるため、より保守しやすくなります。今まで標準以外の O/R マッパを使っていた方々も、標準でここまで簡単にできるようになっています。是非 JPA 2.0 を今一度ご検討ください。

JPA 2.0 の新機能はこちらもご参照ください。

私が参考にする Web Page
Java Persistence

おすすめ書籍

Entry filed under: Application Server/GlassFish, GlassFish, Java.

Java SE/EE の今後について JavaOne Tokyo 2012 Call For Papers 募集中

2件のコメント


Java Champion & Evangelist

Translate

ご注意

このエントリは個人の見解であり、所属する会社の公式見解ではありません

カレンダー

2011年12月
 1234
567891011
12131415161718
19202122232425
262728293031  

カテゴリー

clustermap

ブログ統計情報

  • 1,299,735 hits

Feeds

アーカイブ