JDO 2.3 與 App Engine 搭配使用

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

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

App Engine Java SDK 包含 App Engine Datastore 的 JDO 2.3 實作。這是一項以 DataNucleus Access Platform 1.0 版為基礎,且適用於 JDO 2.3 的開發原始碼參照實作。

附註:此頁面提供的操作說明適用於 JDO 2.3 版,這個版本的 JDO 使用 App Engine 適用的 1.0 版 DataNucleus 外掛程式。App Engine 現在提供 DataNucleus 2.x 外掛程式,可讓您執行 JDO 3.0。新的外掛程式支援非從屬關聯性,並提供許多新的 API 和功能。這個升級版本與舊版本無法完全回溯相容。如果您使用 JDO 3.0 重新建構應用程式,則必須更新及重新測試程式碼。如要進一步瞭解新版本,請參閱 JDO 3.0。如要進一步瞭解升級內容,請參閱 JDO 3.0 與 App Engine 搭配使用

設定 JDO 2.3

如要使用 JDO 存取資料儲存庫,App Engine 應用程式必須符合以下要求:

  • JDO 及 DataNucleus App Engine 外掛程式 JAR 必須位在應用程式的 war/WEB-INF/lib/ 目錄中。
  • 名稱為 jdoconfig.xml 的設定檔必須位在應用程式的 war/WEB-INF/classes/META-INF/ 目錄中,其中包含指示 JDO 使用 App Engine 資料儲存庫的設定。
  • 專案的建構程序必須對已編譯的資料類別執行後編譯「強化」步驟,才能將這些資料類別與 JDO 實作建立關聯。

如果您使用 Apache Ant 建構專案,可透過 SDK 內含的 Ant 工作來執行強化步驟;此外,您必須在設定專案時複製 JAR 並建立設定檔。

複製 JAR

JDO 與資料儲存庫 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 來存取資料儲存庫。

建立 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.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
        <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>
</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.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
        <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.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
        <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" />
    </persistence-manager-factory>
</jdoconfig>

如要瞭解如何使用已命名的設定集來建立 PersistenceManager,請參閱下方的取得 PersistenceManager 執行個體

強化資料類別

JDO 可使用建構程序中的後編譯「強化」步驟,將資料類別與 JDO 實作建立關聯。

如果您使用的是 Apache Ant,則 SDK 內含的 Ant 工作會執行這個步驟。

您可以在指令列使用下列指令,對已編譯的類別執行強化步驟:

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 執行個體 (使用相同設定名稱) 時,將擲回例外狀況。有一個簡單的方式可管理 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() 方法。在呼叫 close() 方法後使用 PersistenceManager 執行個體會發生錯誤。

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

不支援的 JDO 2.3 功能

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

  • 非從屬關聯性。您可以使用明確的金鑰值來實作非從屬關聯性。不過,日後的版本可能會支援適用於非從屬關聯性的 JDO 語法。
  • 多對多從屬關聯性。
  • 「彙整」查詢。當您對父項種類執行查詢時,不可在篩選器中使用子項實體的欄位。提醒您,您可以使用金鑰直接在查詢中測試父項的關聯性欄位。
  • JDOQL 分組和其他匯總查詢。
  • 多型態查詢。您無法透過查詢類別來取得子類別的執行個體。每個類別均由資料儲存庫中的個別實體種類來代表。
  • @PersistenceCapable 註解中使用 IdentityType.DATASTORE。僅支援 IdentityType.APPLICATION
  • 目前已知有一個錯誤會造成無法建立同類別父項和子項的一對多從屬關聯性,因而難以建立樹狀結構的模型。我們將在日後的新版本中修正這個錯誤。目前您暫時可透過為父項或子項儲存明確金鑰值來避開這個問題。

停用交易和移轉現有的 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.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
        <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 適用的 App Engine 標準環境