將 JDO 3.0 與 App Engine 搭配使用

Java 資料物件 (JDO) 是一種可以在 Java 中存取資料庫的標準介面,可讓您對應 Java 類別與資料庫表格。透過開放原始碼外掛程式,即可將 JDO 與 Datastore 搭配使用。此頁面提供如何開始使用此外掛程式的相關資訊。

警告:我們認為,大多數開發人員如果使用的是低階 Datastore API 或專為 Datastore 開發的其中一種開放原始碼 API (例如,Objectify),可以得到更好的體驗。JDO 專為與傳統關聯資料庫搭配使用而設計,因此無法明確指出 Datastore 與關聯資料庫不同的部分,例如實體群組與祖系查詢。這會導致發生難以瞭解及修正的小問題。

App Engine Java SDK 包含 App Engine 適用的 2.x 版 DataNucleus 外掛程式。這個外掛程式與 3.0 版 DataNucleus Access Platform 對應,可讓您透過 JDO 3.0 使用 App Engine Datastore。

如要進一步瞭解 JDO,請參閱 Access Platform 3.0 說明文件,尤其是 JDO 對應JDO API 部分。

警告:App Engine 適用的 DataNucleus 外掛程式 2.x 使用 DataNucleus v3.x。這個新的外掛程式與之前的 (1.x) 外掛程式並非完全回溯相容。如果您升級至新版本,請確保更新及測試您的應用程式。

建構支援 JDO 2.x 與 3.0 的工具

您可以藉由 Apache Ant 或 Maven,使用 App Engine 適用的 2.x 或 3.0 版本 DataNucleus 外掛程式:

  • 針對 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>

遷移至 2.x 版本的 DataNucleus 外掛程式

這部分提供的操作說明,可將您的應用程式升級為使用與 DataNucleus Access Platform 3.0 和 JDO 3.0 對應的 App Engine 適用 2.x 版本 DataNucleus 外掛程式。這個外掛程式與 1.x 外掛程式並非完全回溯相容,且可能會發生變更。如果您升級,請確保更新及測試您的應用程式程式碼。

新預設行為

2.x 版本的 App Engine DataNucleus 外掛程式與之前的版本相比,有一些不同的預設值:

  • 非交易呼叫 PersistenceManager.makePersistent()PersistenceManager.deletePersistent() 現在會自動執行。(這些函式之前在下一個交易中或在執行 PersistenceManager.close() 時執行。)
  • 現在,重複的 PersistenceManagerFactory (PMF) 分配不會再發生例外狀況。如果您將持續性屬性 datanucleus.singletonPMFForName 設定為「true」,則會傳回目前為該名稱分配的單例 PMF。
  • 現在支援非從屬關聯性。請參閱非從屬關聯性一文。

設定檔的變更

如要將您的應用程式升級為 2.x 版的 App Engine DataNucleus 外掛程式,您需要在 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

必須變更 copyjars 目標才能使用 DataNucleus 2.x:

  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/ 目錄中,其中包含讓 JDO 使用 App Engine 資料儲存庫的設定。
  • 專案的建構程序必須對編譯的資料類別執行後編譯「強化」步驟,才能將其與 JDO 實作建立關聯。

複製 JAR

JDO 與資料儲存庫 JAR 包含在 App Engine Java SDK 中,您可以在 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 建立管理員時生效的設定值。您也可以覆寫單一 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" />

同一個 jdoconfig.xml 檔案可以有多個 <persistence-manager-factory> 元素,並使用不同的 name 屬性,這樣就能在相同的應用程式中使用具有不同設定的 PersistenceManager 執行個體。例如,下列 jdoconfig.xml 檔案會建立兩組設定,一組命名為 "transactions-optional",另一組命名為 "eventual-reads-short-deadlines"

<?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 類別的執行個體執行實例化作業並呼叫方法。Factory 會使用 JDO 設定來建立 PersistenceManager 執行個體。

由於 PersistenceManagerFactory 執行個體需要花費時間進行初始化,因此應用程式應重複使用單一執行個體。有一個簡單的方式可管理 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 的單例執行個體。

應用程式會使用 Factory 執行個體,為每個存取資料儲存庫的要求建立一個 PersistenceManager 執行個體。

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

import PMF;

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

您可以使用 PersistenceManager 來儲存、更新和刪除資料物件,以及執行資料儲存庫查詢。

完成 PersistenceManager 執行個體的作業後,您必須呼叫該執行個體的 close() 方法。在呼叫 PersistenceManager 執行個體的 close() 方法之後使用該執行個體會發生錯誤。

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

不支援的 JDO 3.0 功能

App Engine 實作不支援以下的 JDO 介面功能:

  • 多對多從屬關聯性。
  • 「彙整」查詢。當您對父項種類執行查詢時,不可在篩選器中使用子項實體的欄位。提醒您,您可以使用金鑰直接在查詢中測試父項的關聯性欄位。
  • JDOQL 分組和其他匯總查詢。
  • 多型態查詢。您無法透過查詢類別來取得子類別的執行個體。每個類別是由資料儲存庫中的個別實體種類代表。

停用交易並移植現有的 JDO 應用程式

我們建議使用的 JDO 設定可將名為 datanucleus.appengine.autoCreateDatastoreTxns 的屬性設為 true。這是 App Engine 專用的屬性,此屬性會指示 JDO 實作將資料儲存庫交易與透過應用程式程式碼管理的 JDO 交易建立關聯。如果您是從頭開始建構新的應用程式,您可能需要使用這個屬性;但是,如果您現在已擁有以 JDO 為基礎的應用程式,且想在 App Engine 上執行該應用程式,您可能會想使用替代持續性設定,進而將此屬性值設定為 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>

由於您只能在一個交易中對屬於相同實體群組的物件執行作業,因此這個方法可能很實用。使用傳統資料庫建構的應用程式通常會假設可在資料庫中使用全域交易功能,讓您可在交易內部更新任何一組記錄。由於 App Engine 資料儲存庫不支援全域交易功能,因此,如果程式碼假設為可使用全域交易功能,App Engine 會擲回例外狀況。如要避免這個例外狀況,您不必在可能非常龐大的程式碼基底中逐一尋找交易管理程式碼,然後將這些程式碼全數移除,只要停用資料儲存庫交易功能即可。雖然這種作法沒有處理程式碼的假設:修訂多個記錄時,不可只完成一部分;但是這種作法能讓您的應用程式正常運作,如此一來,您就可專注在交易程式碼的重構作業並可視情況以漸進方式處理程式碼,不必一次重構所有的交易程式碼。

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Java 8 適用的 App Engine 標準環境