My favorites | 中文(繁體) | Sign in
英文版或許有比此中譯版新的內容

透過應用服務引擎使用 JPA

Java Persistence API (JPA) 是一種標準介面,可將內含資料的物件儲存至關聯式資料庫。這個標準所定義的介面可用於註解 Java 物件、使用查詢擷取物件,以及透過交易與資料庫互動。使用 JPA 介面的應用程式可與不同的資料庫一起運作,不需使用特定廠商的資料庫程式碼。JPA 可讓您的應用程式輕鬆地連接不同的資料庫廠商。

「應用服務引擎 Java SDK」包括適用於「應用服務引擎」資料存放區的 JPA 1.0。實作以 DataNucleus Access Platform 為基礎。因為 JPA 呈現一個標準介面以與關聯式資料庫互動,但「應用服務引擎」資料庫不是關聯式資料庫,所以「應用服務引擎」實作無法支援部分的 JPA 功能。我們會盡可能在需要時提醒您這些功能。

如需 JPA 的詳細資訊,請參閱 Access Platform 1.1 文件。另外,請特別參閱「JPA 對應」和 JPA API

設定 JPA

如要使用 JPA 存取資料存放區,「應用服務引擎」需要下列設定:

  • JPA 和資料庫存放區 JAR 必須在應用程式的 war/WEB-INF/lib/ 目錄中。
  • persistence.xml 設定檔必須位於應用程式的 war/WEB-INF/classes/META-INF/ 目錄,其設定應指示 JPA 使用「應用服務引擎」資料存放區。
  • 專案的建置程序必須在編譯的資料類別上執行編譯後的「強化」步驟,為資料類別與 JPA 實作建立關聯。

如果您使用的是「Eclipse 專用的 Google 外掛程式」,它會為您完成第一跟第三項設定。新建專案精靈會將 JPA 和資料存放區 JAR 放在正確的位置,且建置過程會自動執行「強化」步驟。您還是必須手動建立 persistence.xml,並將它放入 war/WEB-INF/classes/META-INF/。外掛程式也會立即自動上傳它。

如果您是透過 Apache Ant 建置專案,您可以使用 SDK 隨附的 Ant 工作執行強化步驟。設定專案時,您必須複製這些 JAR 並建立設定檔。如需 Ant 工作的詳細資訊,請參閱「使用 Apache Ant」。

複製 JAR

「應用服務引擎 Java SDK」內含 JPA 和資料存放區 JAR,您可以在 appengine-java-sdk/lib/user/orm/ 目錄中找到它們。

將 JAR 複製到應用程式的 war/WEB-INF/lib/ 目錄。

請確認 appengine-api.jar 也在 war/WEB-INF/lib/ 目錄中 (您在建立專案時可能已經複製)。「應用服務引擎」的 DataNucleus 外掛程式會使用這個 JAR 存取資料存放區。

建立 persistence.xml 設定檔

使用·JPA·介面時,必須在應用程式的 war/WEB-INF/classes/META-INF/ 目錄中放置一個名為·persistence.xml 的設定檔。您可以直接在這個位置建立這個檔案,或讓您的建置程序從來源目錄複製這個檔案。

使用下列內容建立檔案:

<?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-*.jargeronimo-jpa-*.jar (其中 * 為每個 JAR 的適當版本號碼),以及所有的資料類別。

如需 DataNucleus 位元組碼強化器的詳細資料,請參閱 DataNucleus 文件

取得 EntityManager 實例

應用程式使用 EntityManager 類別實例與 JPA 互動。您可以在 EntityManagerFactory 類別的實例上,建立方法的實例並呼叫方法,藉此取得這個實例。Factory 使用 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;
    }
}

應用程式使用 factory 實例,為每個存取資料存放區的要求建立一個 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 儲存的每個物件都會成為「應用服務引擎」資料存放區的實體。實體的種類衍生至類別的單一名稱 (不包括套件名稱)。類別的每個持續性欄位表示實體的屬性,而屬性名稱等於欄位名稱 (大小寫相同)。

若要宣告可從 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 類別實例的實例或集合
  • 內嵌類別,在實體中儲存為屬性

資料類別必須有一個公用或受保護的預設建構函式和專門儲存對應資料存放區實體之主索引鍵的欄位。有 4 個不同種類的金鑰欄位可供您選擇,每種使用的值類型和註解都不相同。(如需詳細資訊,請參閱「建立資料:金鑰」)。最簡單的金鑰欄位為長整數值,當物件第一次儲存到資料存放區時,可自動由 JPA 填入跨所有類別實例的唯一值。長整數金鑰使用 @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 介面功能:

  • 擁有多數到多數關係,和解除擁有關係。您可以使用明確的金鑰值來實作解除擁有關係,但不會在 API 中強制執行類型檢查。
  • 「Join」查詢。在父系種類上執行查詢時,您無法在篩選器中使用子系實體的欄位。請注意,您可以使用金鑰,在查詢中直接測試父系的關聯性欄位。
  • 彙總查詢 (group by、having、sum、avg、max、min)
  • 多型態查詢。您無法執行類別的查詢以取得子類別的實例。每個類別是由資料存放區中的個別實體種類代表之。