在 JDO 中建立、取得和刪除資料

將 JDO 資料物件儲存至資料儲存庫,可輕鬆呼叫 PersistenceManager 執行個體的 makePersistent() 方法。App Engine JDO 實作使用物件的主要金鑰欄位來追蹤與資料物件相對應的資料儲存庫實體,並且可自動為新物件產生金鑰。您可以使用金鑰快速擷取實體,也可以用已知值來建構金鑰 (如帳戶 ID)。

將物件設為持續性物件

如要將簡單的資料物件儲存在資料儲存庫中,您可以呼叫 PersistenceManager 的 makePersistent() 方法,為其傳送執行個體。

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

Employee e = new Employee("Alfred", "Smith", new Date());

try {
    pm.makePersistent(e);
} finally {
    pm.close();
}

makePersistent() 的呼叫會同步進行,並在儲存物件及更新索引之後傳回結果。

如要將多個物件儲存在 JDO 中,請以物件集合呼叫 makePersistentAll(...) 方法。這個方法將使用單一低層級批次儲存作業,該作業比一系列個別 makePersistent(...) 叫用更有效。

附註:如有任一資料物件的持續性欄位參照的是其他持續性資料物件,而且這些物件中有任何一個物件未經儲存或經過載入後遭到變更,那麼這些參照的物件也會儲存至資料儲存庫。請參閱關係一文。

金鑰

App Engine 中的每個實體都有專屬的獨特金鑰。完整的金鑰含有以下資訊:應用程式 ID、種類和實體 ID。(金鑰也包含實體群組的相關資訊;詳情請參閱交易一文)。

物件的金鑰儲存在執行個體的欄位中,您可以使用 @PrimaryKey 註解識別主要金鑰欄位。

金鑰的 ID 部分可由應用程式在建立物件時提供字串形式的 ID,也可由資料儲存庫自動產生數字 ID。資料儲存庫中所有實體的完整金鑰皆不得重複,換句話說,所有種類相同且實體群組父項 (如果有的話) 相同的物件都必須具有獨特的 ID。您可使用欄位類型和註解對金鑰指定您要的行為。

如果類別在關係中做為「子項」類別使用,金鑰欄位必須屬於能夠表示實體群組父項的類型:金鑰執行個體,或編碼為字串的金鑰值。如要進一步瞭解實體群組,請參閱交易一文;如要進一步瞭解關係,請參閱關係一文。

提示:如果應用程式建立了新物件且為此物件指定的字串 ID 與同一種類 (且實體群組父項也相同) 的其他物件的字串 ID 相同,則儲存這個新物件會覆寫資料儲存庫中的既有物件。如要在建立新物件之前先偵測某個字串 ID 是否已由其他物件使用,您可試著透過交易取得具有特定 ID 的實體,如果交易結果表示沒有具有該 ID 的實體,您就可以使用這個 ID 建立新物件。請參閱交易一文。

主要金鑰欄位有以下四種類型:

長整數 (java.lang.Long),資料儲存庫自動產生的實體 ID。如果物件沒有實體群組父項,且物件 ID 必須由資料儲存庫自動產生,請使用這種類型。執行個體的長金鑰欄位是在您儲存此執行個體時產生的。

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;
未編碼的字串

字串 (java.lang.String),應用程式在建立物件時提供的實體 ID (「金鑰名稱」)。如果物件沒有實體群組父項,且物件 ID 必須由應用程式提供,請使用這種類型。應用程式會在儲存之前,將此欄位設為偏好的 ID。

import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    private String name;
金鑰

金鑰執行個體 (com.google.appengine.api.datastore.Key)。金鑰值包括實體群組父項 (如果有的話) 的金鑰,以及應用程式指派的字串 ID 或系統產生的數字 ID。如要使用應用程式指派的字串 ID 來建立物件,您可以使用該 ID 建立金鑰值,再將欄位設為此值。如要使用系統指派的數字 ID 來建立物件,您可以將金鑰欄位設為空值。(如要瞭解如何使用實體群組父項,請參閱交易一文)。

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    public void setKey(Key key) {
        this.key = key;
    }

應用程式可使用 KeyFactory 類別建立金鑰執行個體:

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

// ...
        Key key = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
        Employee e = new Employee();
        e.setKey(key);
        pm.makePersistent(e);
編碼字串形式的金鑰

與金鑰類型相似,但其值為編碼字串形式的金鑰。編碼字串金鑰可讓您透過可攜方式編寫應用程式,同時仍可運用 App Engine 資料儲存庫實體群組的種種優點。

import javax.jdo.annotations.Extension;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;

應用程式可以在儲存之前,使用具備名稱的金鑰填入此值,或者將其設為空值。如果編碼金鑰欄位為空值,則儲存物件時,該欄位將填入系統產生的金鑰。

您可以使用 KeyFactory 方法 keyToString()stringToKey(),在金鑰執行個體與編碼字串表示法之間來回轉換。

使用編碼金鑰字串時,您可透過額外欄位提供物件字串或數字 ID 的存取權:

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;

    @Persistent
    @Extension(vendorName="datanucleus", key="gae.pk-name", value="true")
    private String keyName;

    // OR:

    @Persistent
    @Extension(vendorName="datanucleus", key="gae.pk-id", value="true")
    private Long keyId;

您可在儲存物件之前,將 "gae.pk-name" 欄位設定為金鑰名稱。儲存物件之後,編碼金鑰欄位即填入含有金鑰名稱的完整金鑰,其類型必須是 String

儲存物件之後即填入 "gae.pk-id" 欄位且無法修改,其類型必須是 Long。

建立具有所產生金鑰的新物件 (使用 valueStrategy = IdGeneratorStrategy.IDENTITY 的金鑰欄位) 時,其金鑰值一開始為 null。將物件寫入資料儲存庫時,該金鑰欄位即填入一個值。如果您使用的是交易,那麼會在交易修訂後寫入物件。否則,系統會在呼叫 makePersistent() 方法時 (若建立物件),或在呼叫 PersistenceManager 執行個體的 close() 方法時 (若更新物件) 寫入物件。

如要進一步瞭解如何設定金鑰,請參閱實體、屬性和金鑰一文。

透過金鑰取得物件

如要擷取已擁有金鑰的物件,請使用 PersistenceManager 的 getObjectById() 方法。這個方法採用的是物件類別和金鑰:

        Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
        Employee e = pm.getObjectById(Employee.class, k);

如果類別使用的金鑰欄位是未編碼的字串 ID (String) 或數字 ID (Long),getObjectByID() 可採用簡單值做為金鑰參數:

        Employee e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com");

金鑰引數的類型可以是任一支援的金鑰欄位類型 (字串 ID、數字 ID、金鑰值、編碼金鑰字串),也可以是類別中金鑰欄位類型以外的其他類型。App Engine 必須能夠從類別名稱和提供的值衍生出完整金鑰;字串 ID 和數字 ID 兩者互斥,因此,使用數字 ID 的呼叫永遠不會傳回含有字串 ID 的實體。如果使用的是金鑰值或編碼金鑰字串,其金鑰必須參照由類別代表其種類的實體。

更新物件

透過 JDO 更新物件的其中一種方式是擷取物件,然後在傳回物件的 PersistenceManager 尚未關閉前修改該物件。PersistenceManager 關閉之後,變更仍會存續。例如:

public void updateEmployeeTitle(User user, String newTitle) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        Employee e = pm.getObjectById(Employee.class, user.getEmail());
        if (titleChangeIsAuthorized(e, newTitle) {
            e.setTitle(newTitle);
        } else {
            throw new UnauthorizedTitleChangeException(e, newTitle);
        }
    } finally {
        pm.close();
    }
}

由於 Employee 執行個體由 PersistenceManager 傳回,因此 PersistenceManager 瞭解對 Employee 中「持續」欄位所做的任何修改,並會在關閉 PersistenceManager 時,將資料儲存庫自動更新為這些修改內容。PersistenceManager 之所以知道這些,是因為 Employee 執行個體已「附加」至 PersistenceManager。

您可以在 PersistenceManager 關閉之後,透過將類別宣告為「可卸離」來修改物件。如要執行此操作,請將 detachable 屬性新增至 @PersistenceCapable 註解:

import javax.jdo.annotations.PersistenceCapable;

@PersistenceCapable(detachable="true")
public class Employee {
    // ...
}

現在,即使載入 Employee 物件的 PersistenceManager 已關閉,您仍然可以讀取和寫入該物件的欄位。以下範例說明卸離物件的實用性:

public Employee getEmployee(User user) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    Employee employee, detached = null;
    try {
        employee = pm.getObjectById(Employee.class,
            "Alfred.Smith@example.com");

        // If you're using transactions, you can call
        // pm.setDetachAllOnCommit(true) before committing to automatically
        // detach all objects without calls to detachCopy or detachCopyAll.
        detached = pm.detachCopy(employee);
    } finally {
        pm.close();
    }
    return detached;
}

public void updateEmployeeTitle(Employee e, String newTitle) {
    if (titleChangeIsAuthorized(e, newTitle) {
        e.setTitle(newTitle);
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
            pm.makePersistent(e);
        } finally {
            pm.close();
        }
    } else {
        throw new UnauthorizedTitleChangeException(e, newTitle);
    }
}

如果使用卸離物件,即不需要建立資料移轉物件。如要進一步瞭解如何使用卸離物件,請參閱 DataNucleus 說明文件

刪除物件

如要刪除資料儲存庫中的物件,請以下列物件呼叫 PersistenceManager 的 deletePersistent() 方法:

pm.deletePersistent(e);

如要刪除 JDO 中的多個物件,請以物件集合呼叫 deletePersistentAll(...) 方法。這個方法將使用單一低層級批次刪除作業,該作業比一系列個別 deletePersistent(...) 叫用更有效。

如果物件欄位含有同屬持續性的子項物件,則這些子項物件也會遭到刪除。詳情請參閱關係一文。

如要刪除符合查詢的所有物件,您可以使用 JDOQL 的「依查詢刪除」功能。詳情請參閱依查詢刪除實體一文。

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

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

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