JPA的介紹分析
Java持久化API (JPA) 顯著簡(jiǎn)化了Java Bean的持久性并提供了一個(gè)對(duì)象關(guān)系映射方法,該方法使您可以采用聲明方式定義如何通過(guò)一種標(biāo)準(zhǔn)的可移植方式,將Java 對(duì)象映射到關(guān)系數(shù)據(jù)庫(kù)表以及后續(xù)的一系列數(shù)據(jù)持久化行為。
-
JPA可以將任何普通的Java 對(duì)象 (POJO) 類(lèi)指定為 JPA 實(shí)體。
- JPA實(shí)體:一個(gè)應(yīng)使用JPA實(shí)現(xiàn)程序的服務(wù)將其非臨時(shí)字段持久保存到關(guān)系數(shù)據(jù)庫(kù)(在 Java EE EJB 容器的內(nèi)部或在簡(jiǎn)單 Java SE 應(yīng)用程序中的 EJB 容器的外部)的 Java 對(duì)象。
- 可以使用注解配置實(shí)體的JPA行為,注解是一種使用元數(shù)據(jù)修飾 Java 源代碼的簡(jiǎn)單表達(dá)方法,它編譯為相應(yīng)的 Java 類(lèi)文件,以便在運(yùn)行時(shí)由 JPA 持久化機(jī)制提供程序解釋以管理 JPA 行為。
例如,要將Java類(lèi)指定為JPA實(shí)體,請(qǐng)使用@Entity注解:
JPA注解總覽



JPA實(shí)體型注解
@Entity
使用 @Entity 批注將普通的舊式 Java 對(duì)象 (POJO) 類(lèi)指定為實(shí)體,并使其可用于 JPA 服務(wù)。必須將 POJO 類(lèi)指定為實(shí)體,然后才可以使用任何其他 JPA 批注。
image
@Table
默認(rèn)情況下,JPA持續(xù)性提供程序假設(shè)實(shí)體的所有持久字段均存儲(chǔ)到一個(gè)名稱(chēng)為實(shí)體名稱(chēng)的數(shù)據(jù)庫(kù)表中(請(qǐng)參閱 @Entity )。 在以下條件下,使用 @Table注解可以指定與實(shí)體關(guān)聯(lián)的主表:
實(shí)體名稱(chēng)難于處理、是一個(gè)保留字、與預(yù)先存在的數(shù)據(jù)模型不兼容或作為數(shù)據(jù)庫(kù)中的表名無(wú)效需要控制表所屬的目錄或模式。
如果希望 JPA 將某些字段持久保存到主表,而將其他字段持久保存到一個(gè)或多個(gè)輔助表,請(qǐng)參閱@SecondaryTable 。 下表列出了此批注的屬性。有關(guān)更多詳細(xì)信息,請(qǐng)參閱 API 。

@Entity
@Table(name="Model")
public class JavaModel implements Serializable {
...
}
@TableGenerator
- 如果使用 @GeneratedValue 批注指定一個(gè) TABLE 類(lèi)型的主鍵生成器,可以使用 @TableGenerator 批注微調(diào)該主鍵生成器以:
- 由于名稱(chēng)難于處理、是一個(gè)保留字、與預(yù)先存在的數(shù)據(jù)模型不兼容或作為數(shù)據(jù)庫(kù)中的表名無(wú)效而更改主鍵生成器的表名稱(chēng)
- 更改分配大小以匹配應(yīng)用程序要求或數(shù)據(jù)庫(kù)性能參數(shù)
- 更改初始值以匹配現(xiàn)有的數(shù)據(jù)模型(例如,如果基于已經(jīng)為其分配或保留了一組主鍵值的現(xiàn)有數(shù)據(jù)集構(gòu)建)
- 使用特定目錄或模式配置主鍵生成器的表
- 在主鍵生成器表的一列或多列商配置一個(gè)唯一的約束
@TableGenerator 屬性


顯示了如何使用此注解為名為 empGen 的 TABLE 主鍵生成器指定分配大小。
@TableGenerator
@Entity
public class Employee implements Serializable {
@Id
@TableGenerator(
name="empGen",
allocationSize=1
)
@GeneratedValue(strategy=TABLE, generator="empGen")
@Column(name="CUST_ID")
public Long getId() {
return id;
}
...
@Temporal
使用 @Temporal 注解指定 JPA 的提供程序應(yīng)只為 java.util.Date 和 java.util.Calendar 類(lèi)型的字段或?qū)傩猿志帽4娴臄?shù)據(jù)庫(kù)類(lèi)型,可以與 @Basic 一起使用。

示例,顯示了如何使用此批注指定 JPA 持續(xù)性提供程序應(yīng)將 java.util.Date 字段 startDate 持久保存為 DATE ( java.sql.Date ) 數(shù)據(jù)庫(kù)類(lèi)型。
@Entity
public class Employee {
@Temporal(DATE)
protected java.util.Date startDate;
...
}
@Transient
- 默認(rèn)情況下,JPA 持久化提供程序假設(shè)實(shí)體的所有字段均為持久字段。
- 使用 @Transient 注解指定實(shí)體的非持久字段或?qū)傩?,例如,一個(gè)在運(yùn)行時(shí)使用但并非實(shí)體狀態(tài)一部分的字段或?qū)傩浴?/li>
- JPA 提供程序不會(huì)對(duì)批注為 @Transient 的屬性或字段持久保存(或創(chuàng)建數(shù)據(jù)庫(kù)模式)。
- 該注解可以與 @Entity 、@MappedSuperclass 和 @Embeddable 一起使用。
- 該注解沒(méi)有屬性。
@Entity
public class Employee {
@Id
int id;
@Transient
Session currentSession;
...}
@Column
默認(rèn)情況下,JPA 持續(xù)性提供程序假設(shè)每個(gè)實(shí)體的持久字段存儲(chǔ)在其名稱(chēng)與持久字段的名稱(chēng)相匹配的數(shù)據(jù)庫(kù)表列中。
使用 @Column 批注:
將持久字段與其他名稱(chēng)關(guān)聯(lián)(如果默認(rèn)列名難于處理、與事先存在的數(shù)據(jù)模型不兼容或作為數(shù)據(jù)庫(kù)中的列名無(wú)效)
將持久字段與輔助表中的列關(guān)聯(lián)(請(qǐng)參閱 @SecondaryTable )
微調(diào)數(shù)據(jù)庫(kù)中列的特征
@Column 屬性



如何使用此批注使 JPA 將 empId 持久保存到輔助表 EMP_HR 中的列 EMP_NUM 。默認(rèn)情況下,JPA 將 empName 持久保存到主表 Employee 中的列 empName 。
@Column
@Entity
@SecondaryTable(name="EMP_HR")
public class Employee implements Serializable {
@Column(name="EMP_NUM", table="EMP_HR")
private Long empId;
private String empName;
}
@UniqueConstraint
默認(rèn)情況下,JPA持久化提供程序假設(shè)所有列均可以包含重復(fù)值。
使用@UniqueConstraint注解指定將在為主表或輔助表生成的DDL中包含一個(gè)唯一約束,或者,您可以在列級(jí)別指定唯一約束。
屬性狀態(tài)

顯示了如何使用此注解對(duì)主表 EMP 中的列 EMP_ID 和 EMP_NAME 指定一個(gè)唯一約束,使用唯一約束的 @Table。
@Entity
@Table(
name="EMP",
uniqueConstraints={@UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})}
)
public class Employee implements Serializable {
...
}
@Version
默認(rèn)情況下,JPA持久化提供程序假設(shè)應(yīng)用程序負(fù)責(zé)數(shù)據(jù)一致性。
使用@Version注解通過(guò)指定用作其樂(lè)觀鎖定值的實(shí)體類(lèi)的版本字段或?qū)傩詠?lái)啟用 JPA 管理的樂(lè)觀鎖定(推薦做法)。
- 選擇版本字段或?qū)傩詴r(shí),確保:
- 每個(gè)實(shí)體只有一個(gè)版本字段或?qū)傩?/li>
- 選擇一個(gè)持久保存到主表的屬性或字段(請(qǐng)參閱 @Table )
- 您的應(yīng)用程序不修改版本屬性或字段
如何使用此注解將屬性
getVersionNum指定為樂(lè)觀鎖定值。在該示例中,該屬性的列名設(shè)置為OPTLOCK(請(qǐng)參閱 @Column ),而非屬性的默認(rèn)列名。
@Version
@Entity
public class Employee implements Serializable {
...
@Version
@Column(name="OPTLOCK")
protected int getVersionNum() {
return versionNum;
}
...
}
@Embeddable 和 @Embedded
使用 @Embeddable 批注指定一個(gè)類(lèi),該類(lèi)的實(shí)例存儲(chǔ)為擁有實(shí)體的固有部分并共享該實(shí)體的身份。嵌入對(duì)象的每個(gè)持久屬性或字段都將映射到實(shí)體的數(shù)據(jù)庫(kù)表。
類(lèi) EmploymentPeriod 在用作為 @Embedded 的持久字段的類(lèi)型時(shí)可以嵌套到實(shí)體中.
@Embeddable
public class EmploymentPeriod {
java.util.Date startDate;
java.util.Date endDate;
...
}
@Embedded
使用 @Embedded 批注指定一個(gè)持久字段,該字段的 @Embeddable 類(lèi)型可以存儲(chǔ)為擁有實(shí)體的固有部分,并共享該實(shí)體的身份。
嵌入對(duì)象的每個(gè)持久屬性或字段均映射到擁有實(shí)體的數(shù)據(jù)庫(kù)表。
可以結(jié)合使用 @Embedded 和 @Embeddable 以建立嚴(yán)格所有權(quán)關(guān)系的模型,以便在刪除了擁有對(duì)象的情況下還將刪除被擁有的對(duì)象。嵌入的對(duì)象不應(yīng)映射到多個(gè)表。
@Embeddable 類(lèi)中指定的列定義適用于 @Embedded 類(lèi)。
如果要覆蓋這些列定義,請(qǐng)使用 @AttributeOverride 。
@Embeddable 類(lèi) EmploymentPeriod可以使用指定的屬性覆蓋嵌入到實(shí)體類(lèi)中。
@Entity
public class Employee implements Serializable{...
@Embedded
@AttributeOverrides({
@AttributeOverride(name="startDate", column=@Column("EMP_START")),
@AttributeOverride(name="endDate", column=@Column("EMP_END"))
)
public EmploymentPeriod getEmploymentPeriod() {}}
@EmbeddedId
使用 @EmbeddedId 批注指定一個(gè)由實(shí)體擁有的可嵌入復(fù)合主鍵類(lèi)(通常由兩個(gè)或更多基元類(lèi)型或 JDK 對(duì)象類(lèi)型組成)。從原有數(shù)據(jù)庫(kù)映射時(shí)(此時(shí)數(shù)據(jù)庫(kù)鍵由多列組成),通常將出現(xiàn)復(fù)合主鍵。
復(fù)合主鍵類(lèi)具有下列特征:
- 它是一個(gè)普通的舊式 Java 對(duì)象 (POJO) 類(lèi)。
- 它必須為 public ,并且必須有一個(gè) public 無(wú)參數(shù)構(gòu)造函數(shù)。
- 如果使用基于屬性的訪問(wèn),則主鍵類(lèi)的屬性必須為 public 或 protected 。
- 它必須是可序列化的。
- 它必須定義 equals 和 hashCode 方法。
- 這些方法的值相等性的語(yǔ)義必須與鍵映射到的數(shù)據(jù)庫(kù)類(lèi)型的數(shù)據(jù)庫(kù)相等性一致。
@Embeddable
public class EmployeePK implements Serializable{
private String name;
private long id;
public EmployeePK() { }
//setter and getter
public int hashCode() {
return (int) name.hashCode() + id;
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof EmployeePK)) return false;
if (obj == null) return false;
EmployeePK pk = (EmployeePK) obj;
return pk.id == id && pk.name.equals(name);
}
}
@Entity
public class Employee implements Serializable{
EmployeePK primaryKey;
public Employee(){}
@EmbeddedId
public EmployeePK getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(EmployeePK pk){
primaryKey = pk;
}
...
}
@MappedSuperclass
使用 @MappedSuperclass 批注指定一個(gè)實(shí)體類(lèi)從中繼承持久字段的超類(lèi)。當(dāng)多個(gè)實(shí)體類(lèi)共享通用的持久字段或?qū)傩詴r(shí),這將是一個(gè)方便的模式。
您可以像對(duì)實(shí)體那樣使用任何直接和關(guān)系映射批注(如 @Basic 和 @ManyToMany)對(duì)該超類(lèi)的字段和屬性進(jìn)行批注,但由于沒(méi)有針對(duì)該超類(lèi)本身的表存在,因此這些映射只適用于它的子類(lèi)。繼承的持久字段或?qū)傩詫儆谧宇?lèi)的表。
可以在子類(lèi)中使用 @AttributeOverride 或 @AssociationOverride 批注來(lái)覆蓋超類(lèi)的映射配置。
如何使用此批注將 Employee 指定為映射超類(lèi)。如何擴(kuò)展實(shí)體中的此超類(lèi),以及如何在實(shí)體類(lèi)中使用 @AttributeOverride 以覆蓋超類(lèi)中設(shè)置的配置。
@MappedSuperclass
public class Employee {
@Id
protected Integer empId;
@Version
protected Integer version;
@ManyToOne
@JoinColumn(name="ADDR")
protected Address address;
public Integer getEmpId() {
...
}
public void setEmpId(Integer id) {
...
}
public Address getAddress() {
...
}
public void setAddress(Address addr) {
...
}
}
@MappedSuperclass
@Entity
@AttributeOverride(name="address", column=@Column(name="ADDR_ID"))
public class PartTimeEmployee extends Employee {
@Column(name="WAGE")
protected Float hourlyWage;
public PartTimeEmployee() {
...
}
public Float getHourlyWage() {
...
}
public void setHourlyWage(Float wage) {
...
}
}
@EntityListeners
@EntityListeners將一個(gè)或多個(gè)實(shí)體監(jiān)聽(tīng)程序類(lèi)與 @Entity 或 @MappedSuperclass 關(guān)聯(lián),條件是您需要在指定的生命周期事件發(fā)生時(shí)執(zhí)行邏輯。
- 不希望在實(shí)體 API 中公開(kāi)生命周期監(jiān)聽(tīng)程序方法。
- 要在不同的實(shí)體類(lèi)型之間共享生命周期監(jiān)聽(tīng)程序邏輯。
- 當(dāng)實(shí)體或子類(lèi)上發(fā)生生命周期事件時(shí),JPA 持續(xù)性提供程序?qū)幢O(jiān)聽(tīng)程序定義的順序通知每個(gè)實(shí)體監(jiān)聽(tīng)程序,并調(diào)用使用相應(yīng)的生命周期事件類(lèi)型進(jìn)行批注的實(shí)體監(jiān)聽(tīng)程序方法(如果有)。
實(shí)體監(jiān)聽(tīng)程序類(lèi)具有以下特征:
- 它是一個(gè)普通的舊式 Java 對(duì)象 (POJO) 類(lèi)
- 它有一個(gè)或多個(gè)具有以下簽名的回調(diào)方法:
- public void <MethodName>(Object)
- 可以指定參數(shù)類(lèi)型 Object ,或?qū)嶓w監(jiān)聽(tīng)程序?qū)⑴c其關(guān)聯(lián)的實(shí)體類(lèi)的類(lèi)型。
- 它用一個(gè)或多個(gè)生命周期事件批注對(duì)每個(gè)回調(diào)方法進(jìn)行批注。
- 一個(gè)生命周期事件只能與一個(gè)回調(diào)監(jiān)聽(tīng)程序方法關(guān)聯(lián),但某個(gè)給定的回調(diào)監(jiān)聽(tīng)程序方法可以與多個(gè)生命周期事件關(guān)聯(lián)。
- 如果使用實(shí)體監(jiān)聽(tīng)程序,則可以管理哪些實(shí)體監(jiān)聽(tīng)程序使用
@EntityListeners 屬性

顯示了您可以將多個(gè)生命周期事件與給定的實(shí)體監(jiān)聽(tīng)程序類(lèi)方法關(guān)聯(lián),但任何給定的生命周期事件只能在實(shí)體監(jiān)聽(tīng)程序類(lèi)中出現(xiàn)一次。
@Entity
@EntityListeners(value={EmployeePersistListner.class,
EmployeeRemoveListener.class})
public class Employee implements Serializable { ...}
public class EmployeePersistListener {
@PrePersist
public void employee PrePersist(Object employee) {}};
public class EmployeeRemoveListener {
@PreRemove
@PostRemove
public void employeePreRemove(Object employee) { } ...}
@ExcludeDefaultListeners
如果默認(rèn)監(jiān)聽(tīng)程序行為不適用,請(qǐng)使用 @ExcludeDefaultListeners 批注覆蓋(并阻止)針對(duì)給定 @Entity 或 @MappedSuperclass 執(zhí)行的默認(rèn)監(jiān)聽(tīng)程序。
@Entity
@ExcludeDefaultListeners
public class Employee implements Serializable { ...}
