お気に入り | 日本語 | ログイン

App Engine での JPA の使用

Java Persistence API(JPA)は、データを含むオブジェクトをリレーショナル データベースに保存するための標準的なインターフェースです。この標準では、Java オブジェクトへのアノテーションの割り当て、クエリによるオブジェクトの取得、およびトランザクションを使用したデータベースの使用についてのインターフェースを定義しています。JPA インターフェースを使用しているアプリケーションは、データベース固有のコードを使用することなくさまざまな種類のデータベースで作業できます。JPA を使用し、異なるベンダーのデータベースへと簡単に移行できます。

App Engine Java SDK には、App Engine データストア用の JPA 1.0 の実装が含まれます。この実装は DataNucleus Access Platform に基づいています。JPA はリレーショナル データベースを操作するための標準インターフェースですが、App Engine データストアはリレーショナル データベースではないため、App Engine での実装ではサポートできない JPA の機能があります。これらのサポート外の機能についてはできるだけ多くの人に知ってもらうための手段を講じています。

JPA の詳細については、Access Platform 1.1 ドキュメントをご覧ください。特に、JPA マッピングおよび JPA API をご覧ください。

JPA のセットアップ

データストアへのアクセスに JPA を使用するには、App Engine アプリケーションで次の作業を実行する必要があります。

  • JPA およびデータストアの JAR は、アプリケーションの war/WEB-INF/lib/ ディレクトリに配置する必要があります。
  • persistence.xml という名前の設定ファイルは、アプリケーションの war/WEB-INF/classes/META-INF/ ディレクトリに配置する必要があります。この設定により、JPA が App Engine データベースを使用するように設定します。
  • プロジェクトの構築プロセスでは、コンパイル済みのデータ クラスと JPA 実装と関連付けるため、コンパイル後に「拡張」手順を実行する必要があります。

Google Plugin for Eclipse を使用している場合、上記の 1 つ目と 3 つ目の作業を自動的に行います。新規プロジェクト ウィザードが JPA およびデータストアの JAR を正しいディレクトリに配置し、「拡張」手順を自動的に実行します。persistence.xml は手動で作成し、war/WEB-INF/classes/META-INF/ に配置する必要があります。今後のリリースで、プラグインでもこの作業を自動的に実行するようにする予定です。

プロジェクトの構築に Apache Ant を使用している場合、SDK に含まれている Ant タスクを使用し拡張手順を実行できます。プロジェクトのセットアップ時には、JAR をコピーし、設定ファイルを作成する必要があります。Ant タスクの詳細については、Apache Ant の使用をご覧ください。

JAR のコピー

JPA およびデータストアの JAR は App Engine Java SDK に含まれています。これらは appengine-java-sdk/lib/user/orm/ ディレクトリの下にあります。

JAR をアプリケーションの war/WEB-INF/lib/ ディレクトリにコピーします。

appengine-api.jar もまた war/WEB-INF/lib/ ディレクトリにあることを確認します。(プロジェクトの作成時にコピー済みの場合もあります)。App Engine DataNucleus プラグインはこの JAR を使用しデータストアにアクセスします。

persistence.xml ファイルの作成

JPA インターフェースには、persistence.xml ディレクトリ内に war/WEB-INF/classes/META-INF/ という名前の設定ファイルが必要です。ファイルはこの場所に直接作成することができます。また、構築プロセスでソース ディレクトリからこの場所にコピーすることもできます。

ファイルには次の内容が含まれるように作成してください。

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
        http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

    <persistence-unit name="transactions-optional">
        <provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>
        <properties>
            <property name="datanucleus.NontransactionalRead" value="true"/>
            <property name="datanucleus.NontransactionalWrite" value="true"/>
            <property name="datanucleus.ConnectionURL" value="appengine"/>
        </properties>
    </persistence-unit>

</persistence>

データ クラスの拡張

JPA の DataNucleus 実装は、構築プロセスにおいてコンパイル後の「拡張」手順を使用し、データ クラスと JPA 実装を関連付けます。

Apache Ant を使用している場合、SDK にはこの手順を実行する Ant タスクが含まれます。Ant タスク使用の詳細については、Apache Ant の使用をご覧ください。

コマンド ラインで次のコマンドを実行し、コンパイル済みのクラスで拡張手順を実行できます。

java -cp classpath org.datanucleus.enhancer.DataNucleusEnhancer class-files

「classpath」には、ユーザーのデータ クラスに加え、appengine-java-sdk/lib/tools/ ディレクトリからの JAR datastore-core-*.jardatanucleus-enhancer-*.jarasm-*.jar および geronimo-jpa-*.jar* は各 JAR の適切なバージョン番号) が含まれている必要があります。

DataNucleus のバイトコードエンハンサの詳細については、DataNucleus のドキュメントをご覧ください。

EntityManager インスタンスの取得

アプリケーションは、EntityManager クラスのインスタンスを使用して JPA を操作します。このインスタンスは、インスタンス化を行い EntityManagerFactory クラスのメソッドをコールすることで利用可能となります。ファクトリは JPA 設定("transactions-optional" という名前で識別)を使用し EntityManager インスタンスを生成します。

EntityManagerFactory インスタンスは初期化に時間がかかるため、アプリケーションはできるだけ単一のインスタンスを再利用した方がよいでしょう。簡単な方法は、シングルトン ラッパー クラスを静的なインスタンスで作成することです。次に例を示します。

EMF.java

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public final class EMF {
    private static final EntityManagerFactory emfInstance =
        Persistence.createEntityManagerFactory("transactions-optional");

    private EMF() {}

    public static EntityManagerFactory get() {
        return emfInstance;
    }
}

データストアにアクセスするリクエストごとに、アプリケーションはファクトリ インスタンスを使用して 1 つの EntityManager インスタンスを生成します。

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import EMF;

// ...
    EntityManager em = EMF.get().createEntityManager();

EntityManager を使用してデータ オブジェクトの格納、更新、削除、およびデータストアへのクエリを実行します。

EntityManager インスタンスの使用が完了した後は、インスタンスの close() メソッドをコールする必要があります。close() メソッドをコールした後に EntityManager インスタンスを使用するとエラーとなります。

    try {
        // ... do stuff with em ...
    } finally {
        em.close();
    }

クラスとフィールドのアノテーション

JPA が保存した各オブジェクトは、App Engine データストアのエンティティとなります。エンティティの種類は、クラスのシンプルな名前から取得します(パッケージ名を除いたもの)。クラスの永続フィールドは、それぞれエンティティのプロパティを表します。プロパティの名前はフィールドの名前と同じになります(大文字小文字も区別されます)。

JPA を使用してデータストアへの保存、およびデータストアからの取得ができるように Java クラスを宣言するには、クラスに @Entity アノテーションを割り当てます。次に例を示します。

import javax.persistence.Entity;

@Entity
public class Employee {
    // ...
}

データストアに保存するデータ クラスのフィールドは、デフォルトで永続的なタイプとして宣言するか、永続として明示的に宣言する必要があります。DataNucleus のウェブサイトに、JPA の永続性についてのデフォルトの動作をまとめた表が掲載されています。フィールドを明示的に永続的として宣言するには、通常は @Basic アノテーションを割り当てますが、現在はバグにより正常に動作しません。回避策として、代わりに @Enumerated アノテーションを使用してください。

import java.util.Date;
import javax.persistence.Enumerated;

import com.google.appengine.api.datastore.ShortBlob;

// ...
    @Enumerated
    private ShortBlob data;

フィールドのタイプには、次のいずれかを指定します:

  • データストアでサポートされている主な型
  • コア データストア タイプの値のコレクション(java.util.List<...> など)
  • @Entity クラスのインスタンス、またはインスタンスのコレクション
  • エンティティのプロパティとして格納されている埋め込みクラス

データ クラスにはパブリックまたは保護されたデフォルトのコンストラクタが必要です。また、1 つのフィールドには対応するデータストアのエンティティの主キーを保存しなければなりません。キー フィールドは 4 つの種類から選択でき、種類ごとに異なる値型とアノテーションを使って指定します(詳しくは、データの作成、取得、削除: キーをご覧ください)。最もシンプルなキー フィールドは JPA が自動的に生成する long 型の整数値です。この値はクラス内の他のすべてのインスタンス内で一意であり、オブジェクトを初めてデータストアに保存するときに生成されます。長整数型のキーでは、@Id アノテーションと、@GeneratedValue(strategy = GenerationType.IDENTITY) アノテーションを使用します:

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

// ...
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

次に、データ クラスの例を示します:

import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String firstName;

    private String lastName;

    private Date hireDate;

    // Accessors for the fields.  JPA doesn't use these, but your application does.

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    } 
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    } 

    public String getLastName() {
        return lastName;
    } 
    public void setLastName(String lastName) {
        this.lastName = lastName;
    } 

    public String getHireDate() {
        return hireDate;
    } 
    public void setHireDate(Date hireDate) {
        this.hireDate = hireDate;
    } 
}

サポートされていない JPA の機能

JPA インターフェースの次の機能は、App Engine 実装ではサポートしていません。

  • 多対多の所有関係および非所有関係。明示的なキー値を使用して非所有関係を実装できますが、タイプのチェックは API では強制的に実行されません。
  • 「結合」クエリ。親の種類へのクエリを実行中に、フィルタで子エンティティのフィールドは使用できません。キーを使用して親の関係フィールドへ直接クエリを実行できます。
  • アグリケーション クエリ(group by、having、sum、avg、max、min)
  • ポリモーフィック クエリ。サブクラスのインスタンスの取得には、クラスへのクエリを実行できません。各クラスはデータストア内の別個のエンティティの種類として表されます。