Google Code 提供下列語言介面: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
您可以在資料存放區中使用 JDO 來儲存純 Java 的資料物件 (有時稱為 Plain Old Java Object 或 POJO)。經由 PersistenceManager 設定持續性的每個物件都會成為資料存放區中的實體。您可以使用註解來指示 JDO 如何儲存和重新建立資料類別的實例。
注意:舊版 JDO 使用的是 .jdo 的 XML 檔,而不是 Java 註解。JDO 2.3 仍然可以使用這些 XML 檔,但是本文僅說明如何搭配使用 Java 註解和資料類別。
由 JDO 儲存的每個物件都會成為「應用服務引擎」資料存放區中的實體。實體的種類衍生自類別的簡稱 (內部類別使用不含套件名稱的 $ 路徑)。類別的每個持續性欄位表示實體的屬性,而屬性名稱等於欄位名稱 (大小寫相同)。
如要宣告 Java 類別能夠透過 JDO 儲存至資料存放區以及從資料存放區擷取,請為類別設定 @PersistenceCapable 註解。例如:
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
// ...
}
如果要將資料類別儲存至資料存放區,您必須宣告資料類別欄位為持續性欄位。如要宣告欄位為持續性,請為欄位設定 @Persistent 註解:
import java.util.Date;
import javax.jdo.annotations.Persistent;
// ...
@Persistent
private Date hireDate;
如要宣告欄位為非持續性 (欄位不會儲存至資料存放區,也不會在擷取物件時還原),請為欄位設定 @NotPersistent 註解。
提示:如果沒有指定 @Persistent 或 @NotPersistent 註解,JDO 會按照預設將特定類型的欄位指定為持續性,並按照預設將其他類型的欄位指定為非持續性。如需此預設行為的完整說明,請參閱 DataNucleus 文件。並非所有「應用服務引擎」資料存放區的核心值類型都會按照 JDO 規格預設為持續性,因此建議您將欄位明確註解為 @Persistent 或 @NotPersistent 以便釐清。
欄位可以是下列任何一種類型。這些類型的詳述如下。
java.util.List<...>) 或值陣列@PersistenceCapable 類別的實例或實例集合資料類別必須具備一個欄位,專門儲存相應資料存放區實體的主要金鑰。您可以從四種不同的金鑰欄位種類選擇一種,每個種類使用的值類型和註解都不同 (如需詳細資訊,請參閱「建立資料:金鑰」)。最簡單的金鑰欄位是長整數值,JDO 會在物件首次儲存至資料存放區時,自動填入該類別所有實例中的唯一值。長整數金鑰使用 @PrimaryKey 註解,以及 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) 註解:
提示:將所有的持續性欄位設定為 private 或 protected (或 package protected),並且只提供透過 accessor 方法的公開存取。從另一個類別直接存取持續性欄位可能會略過 JDO 類別強化。或者,您可以將其他類別設定為 @PersistenceAware。如需詳細資訊,請參閱 DataNucleus 文件。
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PrimaryKey;
// ...
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
下列為資料類別的範例:
import java.util.Date;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
@Persistent
private String firstName;
@Persistent
private String lastName;
@Persistent
private Date hireDate;
public Employee(String firstName, String lastName, Date hireDate) {
this.firstName = firstName;
this.lastName = lastName;
this.hireDate = hireDate;
}
// Accessors for the fields. JDO doesn't use these, but your application does.
public Long getId() {
return 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;
}
}
資料存放區支援下列核心值類型:
| 類型 | Java 類別 | 排序順序 | 附註 |
|---|---|---|---|
| 小於 500 位元組的短文字字串 | java.lang.String |
Unicode | 大於 500 位元組的值會擲回 JDOFatalUserException。 |
| 小於 500 位元組的短位元組字串 | com.google.appengine.api.datastore.ShortBlob |
位元組順序 | 大於 500 位元組的值會擲回 JDOFatalUserException。 |
| 布林值 | boolean 或 java.lang.Boolean |
false < true |
|
| 整數 | short、java.lang.Short、int、java.lang.Integer、long、java.lang.Long |
數字 | 儲存為長整數,然後轉換成欄位類型。超出範圍的值會發生溢位。 |
| 浮點數 | float、java.lang.Float、double、java.lang.Double |
數字 | 儲存為兩倍寬度浮點數,然後轉換成欄位類型。超出範圍的值會發生溢位。 |
| 日期時間 | java.util.Date |
依時間順序 | |
| Google 帳戶 | com.google.appengine.api.users.User |
依電子郵件地址 (Unicode) | |
| 長文字字串 | com.google.appengine.api.datastore.Text |
(無法排序) | 不會編製索引。 |
| 長位元組字串 | com.google.appengine.api.datastore.Blob |
(無法排序) | 不會編製索引。 |
| 實體金鑰 | com.google.appengine.api.datastore.Key,或參照物件 (做為子系) |
依路徑元素 (種類、ID 或名稱、種類、ID 或名稱...) | |
| URL | com.google.appengine.api.datastore.Link |
Unicode |
注意:資料存放區支援數種額外的核心值類型,這些類型尚未在 Java Datastore API (Java 資料存放區 API) 中實作,不過已經在 Python API 中實作。如果屬性擁有任何一個上述類型的值,且實體已經載入具備該屬性欄位的物件,則 JDO 將忽略該屬性:如果欄位類型不接受空值,JDO 會將欄位設為 null 或擲回 NullPointerException。 不支援的類型為:Category、Email、IM、PhoneNumber、PostalAddress、Rating、GeoPt。
如要代表包含單一核心類型值的屬性,請宣告 Java 類型欄位,並使用 @Persistent 註解:
import java.util.Date;
import javax.jdo.annotations.Persistent;
// ...
@Persistent
private Date hireDate;
欄位值可以包含 Serializable 類別的實例,將序列化的實例值儲存在 Blob 類型的單一屬性值中。如要告知 JDO 將值序列化,請在欄位使用註解 @Persistent(serialized=true)。Blob 值不會編製索引,也無法用於查詢篩選器和排序順序。
下方的簡易 Serializable 類別範例代表一個檔案,包括檔案內容、檔名和 MIME 類型。這不是 JDO 資料類別,因此沒有持續性註解。
import java.io.Serializable;
public class DownloadableFile implements Serializable {
private byte[] content;
private String filename;
private String mimeType;
// ... accessors ...
}
如要將 Serializable 類別的實例儲存為屬性的 Blob 值,請宣告類型為此類別的欄位,並使用 @Persistent(serialized = "true") 註解:
import javax.jdo.annotations.Persistent;
import DownloadableFile;
// ...
@Persistent(serialized = "true")
private DownloadableFile file;
如果欄位值是 @PersistenceCapable 類別的實例,則會在兩個物件之間建立一對一的從屬關聯性。如果欄位是該等參照的集合,則會建立一對多的從屬關聯性。
重要事項:從屬關聯性隱含交易、實體群組和階層式刪除。如需詳細資訊,請參閱「交易」和「關聯性」。
下方的簡單範例將說明 Employee 物件和 ContactInfo 物件之間的一對一從屬關聯性:
ContactInfo.java
import com.google.appengine.api.datastore.Key;
// ... imports ...
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class ContactInfo {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private String streetAddress;
@Persistent
private String city;
@Persistent
private String stateOrProvince;
@Persistent
private String zipCode;
// ... accessors ...
}
Employee.java
import ContactInfo;
// ... imports ...
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
@Persistent
private ContactInfo myContactInfo;
// ... accessors ...
}
在這個範例中,如果應用程式建立 Employee 實例,並在實例的 myContactInfo 欄位填入新的 ContactInfo 實例,然後使用 pm.makePersistent(...) 儲存 Employee 實例,則資料存放區會建立兩個實體。其中一個實例的種類為 "ContactInfo",代表 ContactInfo 實例。另一個實例的種類為 "Employee"。ContactInfo 實體的金鑰會將 Employee 實體的金鑰當做其實體群組父系。
透過內嵌類別,您不需要建立新的資料存放區實體與設定關聯性,即可使用類別建立欄位值的模型。物件值的欄位會直接儲存在包含物件的資料存放區實體中。
任何一個 @PersistenceCapable 資料類別均可做為另一個資料類別的內嵌物件。該類別的 @Persistent 欄位會內嵌於物件中。如果您將類別設為內嵌 @EmbeddedOnly 註解,則該類別只能做為內嵌類別。因為內嵌類別不會被儲存為個別實體,所以不需要主要金鑰欄位。
下方為內嵌類別的範例。在這個範例中,資料類別將使用的內嵌類別設定為其內部類別;這個做法十分實用,但是在設定內嵌類別時不一定要這麼做。
import javax.jdo.annotations.Embedded;
import javax.jdo.annotations.EmbeddedOnly;
// ... imports ...
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class EmployeeContacts {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Long id;
@PersistenceCapable
@EmbeddedOnly
public static class ContactInfo {
@Persistent
private String streetAddress;
@Persistent
private String city;
@Persistent
private String stateOrProvince;
@Persistent
private String zipCode;
// ... accessors ...
}
@Persistent
@Embedded
private ContactInfo homeContactInfo;
}
內嵌類別的欄位會被儲存為實體屬性,並採用每個欄位的名稱和相應屬性的名稱。如果您的物件類型為內嵌類別,且擁有超過一個欄位,您必須重新命名內嵌類別或物件欄位,以免彼此衝突。您可以在 @Embedded 註解使用引數來指定新的欄位名稱。例如:
@Persistent
@Embedded
private ContactInfo homeContactInfo;
@Persistent
@Embedded(members = {
@Persistent(name="streetAddress", columns=@Column(name="workStreetAddress")),
@Persistent(name="city", columns=@Column(name="workCity")),
@Persistent(name="stateOrProvince", columns=@Column(name="workStateOrProvince")),
@Persistent(name="zipCode", columns=@Column(name="workZipCode")),
})
private ContactInfo workContactInfo;
同樣地,物件上的欄位也不能使用可能與內嵌類別的欄位衝突的名稱,除非內嵌欄位已經重新命名。
由於內嵌類別的持續性屬性會儲存在與其他欄位相同的實體中,因此您可以在 JDOQL 查詢篩選器和排序順序中,使用內嵌類別的持續性欄位。如要指向內嵌欄位,您可以輸入外部欄位的名稱、句號 (.) 加上內嵌欄位的名稱。無論內嵌欄位的屬性名稱是否已經由 @Column 註解變更,這個方法都行得通。
select from EmployeeContacts where workContactInfo.zipCode == "98105"
資料存放區屬性可以擁有超過一個的值。在 JDO 中,這種屬性是由 Collection (集合) 類型的單一欄位代表之,而「集合」是一種核心值類型或是 Serializable 類別。支援的「集合」類型如下:
java.util.ArrayList<...>java.util.HashSet<...>java.util.LinkedHashSet<...>java.util.LinkedList<...>java.util.List<...>java.util.Set<...>java.util.SortedSet<...>java.util.Stack<...>java.util.TreeSet<...>java.util.Vector<...>如果欄位宣告為 List,則資料存放區傳回的物件將會具備 ArrayList 值。如果欄位宣告為 Set,則資料存放區會傳回 HashSet。如果欄位宣告為 SortedSet,則資料存放區會傳回 TreeSet。
例如,類型為 List<String> 的欄位會被儲存為零或多個的屬性字串值,一個 List 的值一個字串值。
import java.util.List;
// ... imports ...
// ...
@Persistent
List<String> favoriteFoods;
(@PersistenceCapable 類別的) 子系物件的「集合」會建立多個一對多關聯性的實體。請參閱「關聯性」。
多重值的資料存放區屬性擁有特殊的查詢篩選器和排序順序行為。如需詳細資訊,請參閱「查詢和索引:擁有多個值的排序順序和屬性」。
「應用服務引擎」資料存放區會區別沒有指定屬性的實體以及擁有 null 屬性值的實體。JDO 則不支援這個區別:物件的每個欄位都擁有一個值,且可能是 null。如果欄位擁有可接受空值的值類型 (內建類型以外的類型,例如 int 或 boolean),並設為 null,則儲存物件時,產生的實體會將該屬性設為空值。
當資料存放區實體載入物件,卻缺少物件其中一個欄位的屬性時,如果該欄位的類型為可接受空值的單一值類型,則該欄位會設為 null。當物件存回資料存放區時,null 屬性在資料存放區中會設為空值。如果欄位不是可接受空值的值類型,則載入缺少相應屬性的實體會擲回例外狀況。如果使用重新建立實例時所使用的相同 JDO 類別建立實體,上述情況就不會發生,但是如果 JDO 類別變更,或是如果改用低階 API 取代 JDO 建立實體,則上述情況就可能發生。
如果欄位的類型為核心資料類型的「集合」或 Serializable 類別,而且實體上沒有屬性值,則屬性會設為單一空值,代表資料存放區中的空集合。如果欄位的類型為陣列類型,將被指派 0 元素的陣列。如果物件已載入且沒有屬性值,則欄位將被指派適當類型的空集合。在內部,資料存放區能夠區別空集合以及擁有空值的集合。
如果實體某個屬性在物件中沒有相應欄位,則無法從物件存取該屬性。如果物件存回至資料存放區,多餘的屬性將遭到刪除。
如果實體某個屬性的值類型與物件中的相應欄位不同,則 JDO 會嘗試將該值轉化為欄位類型。如果無法將該值轉化為欄位類型,JDO 會擲回 ClassCastException。 如果值為數字 (長整數和兩倍寬度浮點數),JDo 會嘗試轉換而非轉化值。如果數字屬性值大於欄位類型,則轉換會發生溢位,但不會擲回例外狀況。