App Engine での JDO 3.0 の使用

Java Data Objects(JDO)は、Java でデータベースにアクセスするための標準インターフェースであり、Java のクラスとデータベースのテーブルとの間のマッピングを提供します。Datastore で JDO を使用するために利用できるオープンソースのプラグインがあります。このページでは、そのプラグインを使う方法について説明します。

警告: ほとんどのデベロッパーは、低水準の Datastore API を使用するか、Datastore 専用に開発された Objectify などのオープンソースの API を使用するほうが快適に作業できるでしょう。JDO は、従来型のリレーショナル データベースに使用するように設計されているため、リレーショナル データベースとは異なる Datastore の特性(エンティティ グループや祖先クエリなど)を明示的に表す方法がありません。これは、理解や修正が困難な微妙な問題の原因になり得ます。

App Engine Java SDK には、App Engine 用 DataNucleus プラグインのバージョン 2.x が含まれています。このプラグインは、DataNucleus Access Platform のバージョン 3.0 に対応しているため、JDO 3.0 経由で App Engine Datastore を使用できます。

JDO の詳細は、Access Platform 3.0 のドキュメントをご覧ください。特に、JDO マッピングJDO API をご覧ください。

警告: App Engine 用 DataNucleus プラグイン 2.x は DataNucleus バージョン 3.x を使用します。この新しいプラグインには、以前の(1.x)プラグインとの完全な下位互換性はありません。新しいバージョンにアップグレードする場合は、アプリケーションの更新とテストを実行するようにしてください。

JDO 2.x および 3.0 をサポートするビルドツール

Apache Ant または Maven を使用して、App Engine に DataNucleus プラグインのバージョン 2.x または 3.0 を使用できます。

  • Ant ユーザーの場合: SDK には、拡張手順を実行するための Ant タスクが含まれています。プロジェクトをセットアップするときに、JAR をコピーして構成ファイルを作成する必要があります。
  • Maven ユーザーの場合: pom.xml ファイルで次のように構成することで、クラスを拡張できます。
    <plugin>
        <groupId>org.datanucleus</groupId>
        <artifactId>maven-datanucleus-plugin</artifactId>
        <version>3.2.0-m1</version>
        <configuration>
            <api>JDO</api>
            <props>${basedir}/datanucleus.properties</props>
            <verbose>true</verbose>
            <enhancerName>ASM</enhancerName>
        </configuration>
        <executions>
            <execution>
                <phase>process-classes</phase>
                <goals>
                    <goal>enhance</goal>
                </goals>
            </execution>
        </executions>
        <dependencies>
            <dependency>
                <groupId>org.datanucleus</groupId>
                <artifactId>datanucleus-core</artifactId>
                <version>3.1.3</version>
            </dependency>
        </dependencies>
    </plugin>

DataNucleus プラグインのバージョン 2.x への移行

このセクションでは、DataNucleus Access Platform 3.0 と JDO 3.0 に対応する App Engine 用 DataNucleus プラグインのバージョン 2.x を使用するように、アプリをアップグレードする手順について説明します。このプラグインには、1.x プラグインとの完全な下位互換性はなく、変更される可能性があります。アップグレードする場合は、アプリケーションのコードを更新してテストするようにしてください。

新しいデフォルト動作

App Engine DataNucleus プラグインのバージョン 2.x には、以前のバージョンとは異なるデフォル動作が設定されています。

  • PersistenceManager.makePersistent()、および PersistenceManager.deletePersistent() への非トランザクション呼び出しは、自動的に実行されるようになりました(以前は、これらの関数が次のトランザクションまたは PersistenceManager.close() で実行されていました)。
  • PersistenceManagerFactory(PMF)の割り当てが重複しても例外が発生しなくなりました。代わりに、永続プロパティの datanucleus.singletonPMFForName を true に設定しておくと、その名前が現在割り当てられているシングルトン PMF が返されるようになります。
  • 非所有関係がサポートされるようになりました。非所有関係を参照してください。

設定ファイルの変更

App Engine DataNucleus プラグインのバージョン 2.x にアプリケーションをアップグレードするには、build.xmljdoconfig.xml の設定を変更する必要があります。

警告: 設定の更新後には、アプリケーション コードをテストして、下位互換性が確保されるようにする必要があります。新しいアプリケーションをセットアップしていて最新バージョンのプラグインを使用する場合は、JDO 3.0 のセットアップに進んでください。

  1. PersistenceManagerFactoryClass プロパティが変更されました。jdoconfig.xml 内の次の行を見つけて、

    <property name="javax.jdo.PersistenceManagerFactoryClass"
              value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>

    次のように変更します
    <property name="javax.jdo.PersistenceManagerFactoryClass"
              value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>

build.xml

DataNucleus 2.x に対応させるには、copyjars ターゲットを変更する必要があります。

  1. copyjars ターゲットが変更されました。次のセクションを探します。
    <target name="copyjars"
        description="Copies the App Engine JARs to the WAR.">
      <mkdir dir="war/WEB-INF/lib" />
      <copy
          todir="war/WEB-INF/lib"
          flatten="true">
        <fileset dir="${sdk.dir}/lib/user">
          <include name="**/*.jar" />
        </fileset>
      </copy>
    </target>
    このセクションを次のように変更します。
    <target name="copyjars"
        description="Copies the App Engine JARs to the WAR.">
      <mkdir dir="war/WEB-INF/lib" />
      <copy
          todir="war/WEB-INF/lib"
          flatten="true">
        <fileset dir="${sdk.dir}/lib/user">
            <include name="**/appengine-api-1.0-sdk*.jar" />
        </fileset>
        <fileset dir="${sdk.dir}/lib/opt/user">
          <include name="appengine-api-labs/v1/*.jar" />
          <include name="jsr107/v1/*.jar" />
          <include name="datanucleus/v2/*.jar" />
        </fileset>
      </copy>
    </target>
  2. datanucleusenhance ターゲットが変更されました。次のセクションを探します。
    <target name="datanucleusenhance" depends="compile"
        description="Performs enhancement on compiled data classes.">
      <enhance_war war="war" />
    </target>
    次のように更新します
    <target name="datanucleusenhance" depends="compile"
        description="Performs enhancement on compiled data classes.">
        <enhance_war war="war">
                <args>
                <arg value="-enhancerVersion"/>
                <arg value="v2"/>
            </args>
        </enhance_war>
    </target>

JDO 3.0 のセットアップ

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

  • JDO と DataNucleus プラグインの JAR は、アプリの war/WEB-INF/lib/ ディレクトリに配置する必要があります。
  • jdoconfig.xml という設定ファイルをアプリの war/WEB-INF/classes/META-INF/ ディレクトリに配置し、App Engine Datastore を使用するよう JDO に指示する設定を行う必要があります。
  • プロジェクトのビルドプロセスで、コンパイル後の「拡張」手順をコンパイル済みのデータクラスに対して実行して、JDO 実装に関連付ける必要があります。

JAR のコピー

JDO とデータストアの JAR は、App Engine Java SDK に含まれています。これらの JAR は appengine-java-sdk/lib/opt/user/datanucleus/v2/ ディレクトリにあります。

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

appengine-api.jar ディレクトリには war/WEB-INF/lib/ も配置してください(プロジェクトの作成時にすでにコピーしている場合もあります)。App Engine DataNucleus プラグインは、この JAR を使用してデータストアにアクセスします。

jdoconfig.xml ファイルの作成

JDO インターフェースには、アプリケーションの war/WEB-INF/classes/META-INF/ ディレクトリ内にある jdoconfig.xml という設定ファイルが必要です。このファイルは、この場所に直接作成することも、ビルドプロセスでソース ディレクトリからコピーすることもできます。

作成するファイルには次の内容を含めます。

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">

    <persistence-manager-factory name="transactions-optional">
        <property name="javax.jdo.PersistenceManagerFactoryClass"
            value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
        <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
        <property name="javax.jdo.option.NontransactionalRead" value="true"/>
        <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
        <property name="javax.jdo.option.RetainValues" value="true"/>
        <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
        <property name="datanucleus.appengine.singletonPMFForName" value="true"/>
    </persistence-manager-factory>
</jdoconfig>

Datastore の読み取りポリシーと呼び出し期限の設定

Datastore のクエリページで説明したように、Datastore の動作は、読み取りポリシー(強整合性と結果整合性)と呼び出し期限を設定することでカスタマイズできます。JDO では、jdoconfig.xml ファイルの <persistence-manager-factory> 要素に目的の値を指定することで、このカスタマイズを実施します。ある PersistenceManager インスタンスで行われるすべての呼び出しには、このマネージャが PersistenceManagerFactory によって作成されたときに指定されていた値が使用されます。また、これらの値は 1 つの Query オブジェクトごとにオーバーライドすることもできます。

PersistenceManagerFactory の読み取りポリシーを設定するには、datanucleus.appengine.datastoreReadConsistency というプロパティを含めます。有効な値は、EVENTUALSTRONG です。未指定の場合のデフォルトは STRONG です(ただし、これらの設定は、特定のエンティティ グループ内の祖先クエリにのみ適用される点に注意してください。祖先以外のクエリは、有効な読み取りポリシーの有無にかかわらず、常に結果整合性になります)。

<property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" />

読み取りと書き込みに個別のデータストア呼び出し期限を設定できます。読み取りには、JDO の標準プロパティ javax.jdo.option.DatastoreReadTimeoutMillis を使用します。書き込みには、javax.jdo.option.DatastoreWriteTimeoutMillis を使用します。値は、ミリ秒単位の時間です。

<property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" />
<property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" />

クロスグループ(XG)トランザクションを使用する場合は、次に示すプロパティを追加します。

<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true" />

異なる name 属性を使用すると、複数の <persistence-manager-factory> 要素を同じ jdoconfig.xml ファイルに含めることができるようになり、同じアプリ内で異なる設定の PersistenceManager インスタンスを使用できるようになります。たとえば、次に示す jdoconfig.xml ファイルでは、"transactions-optional""eventual-reads-short-deadlines" という名前の 2 つの設定のセットを規定しています。

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">

    <persistence-manager-factory name="transactions-optional">
        <property name="javax.jdo.PersistenceManagerFactoryClass"
            value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
        <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
        <property name="javax.jdo.option.NontransactionalRead" value="true"/>
        <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
        <property name="javax.jdo.option.RetainValues" value="true"/>
        <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
    </persistence-manager-factory>

    <persistence-manager-factory name="eventual-reads-short-deadlines">
        <property name="javax.jdo.PersistenceManagerFactoryClass"
            value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
        <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
        <property name="javax.jdo.option.NontransactionalRead" value="true"/>
        <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
        <property name="javax.jdo.option.RetainValues" value="true"/>
        <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>

        <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" />
        <property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" />
        <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" />
        <property name="datanucleus.singletonPMFForName" value="true" />
    </persistence-manager-factory>
</jdoconfig>

名前付きの構成セットで PersistenceManager を作成する方法については、後述の PersistenceManager インスタンスの取得をご覧ください。

データクラスの拡張

JDO はビルドプロセスでコンパイル後の「拡張」手順を使用して、データクラスを JDO の実装に関連付けます。

次に示すコマンドを使用して、コンパイルしたクラスに対する拡張手順をコマンドラインから実行できます。

java -cp classpath com.google.appengine.tools.enhancer.Enhance
class-files

classpath には、appengine-java-sdk/lib/ ディレクトリの JAR appengine-tools-api.jar と、データクラスのすべてが含まれている必要があります。

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

PersistenceManager インスタンスの取得

アプリは、PersistenceManager クラスのインスタンスを使用して JDO を操作します。このインスタンスを取得するには、PersistenceManagerFactory クラスのインスタンスでメソッドをインスタンス化して呼び出します。ファクトリは、JDO 設定を使用して PersistenceManager のインスタンスを作成します。

PersistenceManagerFactory インスタンスの初期化には時間がかかるため、1 つのアプリは 1 つのインスタンスを再利用します。PersistenceManagerFactory インスタンスは、次に示すように、静的なインスタンスでシングルトン ラッパークラスを作成することで管理が簡単になります。

PMF.java

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public final class PMF {
    private static final PersistenceManagerFactory pmfInstance =
        JDOHelper.getPersistenceManagerFactory("transactions-optional");

    private PMF() {}

    public static PersistenceManagerFactory get() {
        return pmfInstance;
    }
}

ヒント: "transactions-optional" は、jdoconfig.xml ファイル内の構成セットの名前を参照します。アプリで複数の設定セットを使用する場合は、目的に応じて JDOHelper.getPersistenceManagerFactory() を呼び出すように、このコードを拡張する必要があります。コードでは、各 PersistenceManagerFactory のシングルトン インスタンスをキャッシュする必要があります。

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

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;

import PMF;

// ...
    PersistenceManager pm = PMF.get().getPersistenceManager();

PersistenceManager は、データ オブジェクトの保存、更新、削除と、データストア クエリの実行に使用します。

PersistenceManager インスタンスによる作業を終了したら、そのインスタンスの close() メソッドを呼び出す必要があります。close() メソッドの呼び出し後に、PersistenceManager インスタンスを使用するとエラーになります。

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

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

次に示す JDO インターフェースの機能は、App Engine の実装ではサポートされていません。

  • 多対多の所有関係。
  • 「結合」クエリ。親の種類に対してクエリを実行する場合は、フィルタで子エンティティのフィールドを使用することはできません。親の関係フィールドは、キーを使用することで、クエリで直接テストすることができます。
  • JDOQL のグループ化とその他の集約クエリ。
  • ポリモーフィック クエリ。クラスのクエリを実行して、サブクラスのインスタンスを取得することはできません。各クラスは、データストア内の個別のエンティティの種類として表されます。

トランザクションの無効化と既存の JDO アプリケーションの移植

JDO 設定では、datanucleus.appengine.autoCreateDatastoreTxns というプロパティを true に設定することをおすすめします。これは App Engine 固有のプロパティであり、データストア トランザクションをアプリケーション コード内で管理される JDO トランザクションに関連付けるように JDO の実装に指示するためのものです。新しいアプリを初めからビルドする場合は、このようにするとよいでしょう。ただし、App Engine での実行が必要な既存の JDO ベースのアプリケーションがある場合は、このプロパティの値を false に設定する別の永続設定の使用が必要になることもあります。

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">

    <persistence-manager-factory name="transactions-optional">
        <property name="javax.jdo.PersistenceManagerFactoryClass"
            value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
        <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
        <property name="javax.jdo.option.NontransactionalRead" value="true"/>
        <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
        <property name="javax.jdo.option.RetainValues" value="true"/>
        <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="false"/>
    </persistence-manager-factory>
</jdoconfig>

この設定が役立つ理由について理解するには、1 つのトランザクション内では同じエンティティ グループに属するオブジェクトにのみオペレーションを実行できることを思い出してください。従来型のデータベースを使用するようにビルドされたアプリケーションは、通常、トランザクション内で任意のレコードのセットを更新できる、グローバル トランザクションを使用できることを想定しています。App Engine データストアはグローバル トランザクションをサポートしていないため、グローバル トランザクションが利用できると想定しているコードでは例外がスローされます。コードベース全体(おそらくは巨大)を見直してトランザクション管理コードをすべて削除する代わりに、単にデータストアのトランザクションを無効にすることができます。これは、複数レコードの変更のアトミック性を想定しているコードに対処することにはなりませんが、アプリを動作させることはできます。それで、トランザクション コードのリファクタリングを一度に全部行うのではなく、段階的に必要に応じて実行できるようになります。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Java の App Engine スタンダード環境