☕【Java技術指南】「JPA編程專題」讓你不再對JPA技術中的“持久化型註解”感到陌生了!

語言: CN / TW / HK

JPA的介紹分析

  • Java持久化API (JPA) 顯著簡化了Java Bean的持久性並提供了一個對象關係映射方法,該方法使您可以採用聲明方式定義如何通過一種標準的可移植方式,將Java 對象映射到關係數據庫表以及後續的一系列數據持久化行為。

  • JPA可以將任何普通的Java 對象 (POJO) 類指定為 JPA 實體。

    • JPA實體:一個應使用JPA實現程序的服務將其非臨時字段持久保存到關係數據庫(在 Java EE EJB 容器的內部或在簡單 Java SE 應用程序中的 EJB 容器的外部)的 Java 對象。
    • 可以使用註解配置實體的JPA行為,註解是一種使用元數據修飾 Java 源代碼的簡單表達方法,它編譯為相應的 Java 類文件,以便在運行時由 JPA 持久化機制提供程序解釋以管理 JPA 行為。
  • 例如,要將Java類指定為JPA實體,請使用@Entity註解:

JPA註解總覽




JPA實體型註解

@Entity

使用 @Entity 批註將普通的舊式 Java 對象 (POJO) 類指定為實體,並使其可用於 JPA 服務。必須將 POJO 類指定為實體,然後才可以使用任何其他 JPA 批註。

@Table

默認情況下,JPA持續性提供程序假設實體的所有持久字段均存儲到一個名稱為實體名稱的數據庫表中(請參閲 @Entity )。 在以下條件下,使用 @Table註解可以指定與實體關聯的主表:

實體名稱難於處理、是一個保留字、與預先存在的數據模型不兼容或作為數據庫中的表名無效需要控制表所屬的目錄或模式。

如果希望 JPA 將某些字段持久保存到主表,而將其他字段持久保存到一個或多個輔助表,請參閲@SecondaryTable 。 下表列出了此批註的屬性。有關更多詳細信息,請參閲 API 。

@Entity
@Table(name="Model") 
public class JavaModel implements Serializable {
    ...
}

@TableGenerator

  • 如果使用 @GeneratedValue 批註指定一個 TABLE 類型的主鍵生成器,可以使用 @TableGenerator 批註微調該主鍵生成器以:
    • 由於名稱難於處理、是一個保留字、與預先存在的數據模型不兼容或作為數據庫中的表名無效而更改主鍵生成器的表名稱
    • 更改分配大小以匹配應用程序要求或數據庫性能參數
    • 更改初始值以匹配現有的數據模型(例如,如果基於已經為其分配或保留了一組主鍵值的現有數據集構建)
    • 使用特定目錄或模式配置主鍵生成器的表
    • 在主鍵生成器表的一列或多列商配置一個唯一的約束
@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 的提供程序應只為 java.util.Date 和 java.util.Calendar 類型的字段或屬性持久保存的數據庫類型,可以與 @Basic 一起使用。

示例,顯示瞭如何使用此批註指定 JPA 持續性提供程序應將 java.util.Date 字段 startDate 持久保存為 DATE ( java.sql.Date ) 數據庫類型。

@Entity
public class Employee {
    @Temporal(DATE)
	protected java.util.Date startDate;
    ...
}

@Transient

  • 默認情況下,JPA 持久化提供程序假設實體的所有字段均為持久字段。
  • 使用 @Transient 註解指定實體的非持久字段或屬性,例如,一個在運行時使用但並非實體狀態一部分的字段或屬性。
  • JPA 提供程序不會對批註為 @Transient 的屬性或字段持久保存(或創建數據庫模式)。
  • 該註解可以與 @Entity 、@MappedSuperclass 和 @Embeddable 一起使用。
  • 該註解沒有屬性。
@Entity
public class Employee {    
	@Id
	int id;    
	@Transient 
	Session currentSession;    
...} 

@Column

默認情況下,JPA 持續性提供程序假設每個實體的持久字段存儲在其名稱與持久字段的名稱相匹配的數據庫表列中。 使用 @Column 批註: 將持久字段與其他名稱關聯(如果默認列名難於處理、與事先存在的數據模型不兼容或作為數據庫中的列名無效) 將持久字段與輔助表中的列關聯(請參閲 @SecondaryTable ) 微調數據庫中列的特徵

@Column 屬性

如何使用此批註使 JPA 將 empId 持久保存到輔助表 EMP_HR 中的列 EMP_NUM 。默認情況下,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

默認情況下,JPA持久化提供程序假設所有列均可以包含重複值。

使用@UniqueConstraint註解指定將在為主表或輔助表生成的DDL中包含一個唯一約束,或者,您可以在列級別指定唯一約束。

屬性狀態

顯示瞭如何使用此註解對主表 EMP 中的列 EMP_ID 和 EMP_NAME 指定一個唯一約束,使用唯一約束的 @Table。

@Entity
@Table(
name="EMP",
uniqueConstraints={@UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})}
)
public class Employee implements Serializable {
    ...
}

@Version

默認情況下,JPA持久化提供程序假設應用程序負責數據一致性。

使用@Version註解通過指定用作其樂觀鎖定值的實體類的版本字段或屬性來啟用 JPA 管理的樂觀鎖定(推薦做法)。

  • 選擇版本字段或屬性時,確保:
    • 每個實體只有一個版本字段或屬性
    • 選擇一個持久保存到主表的屬性或字段(請參閲 @Table )
    • 您的應用程序不修改版本屬性或字段

如何使用此註解將屬性getVersionNum指定為樂觀鎖定值。在該示例中,該屬性的列名設置為OPTLOCK(請參閲 @Column ),而非屬性的默認列名。

@Version

@Entity 
public class Employee implements Serializable {
    ...
	@Version
	@Column(name="OPTLOCK")
	protected int getVersionNum() {
		return versionNum;
	}
    ...
}

@Embeddable 和 @Embedded

  • 使用 @Embeddable 批註指定一個類,該類的實例存儲為擁有實體的固有部分並共享該實體的身份。嵌入對象的每個持久屬性或字段都將映射到實體的數據庫表。

  • 類 EmploymentPeriod 在用作為 @Embedded 的持久字段的類型時可以嵌套到實體中.

@Embeddable
public class EmploymentPeriod {
	java.util.Date startDate;
	java.util.Date endDate;
	...
}

@Embedded

  • 使用 @Embedded 批註指定一個持久字段,該字段的 @Embeddable 類型可以存儲為擁有實體的固有部分,並共享該實體的身份。

  • 嵌入對象的每個持久屬性或字段均映射到擁有實體的數據庫表。

  • 可以結合使用 @Embedded 和 @Embeddable 以建立嚴格所有權關係的模型,以便在刪除了擁有對象的情況下還將刪除被擁有的對象。嵌入的對象不應映射到多個表。

  • @Embeddable 類中指定的列定義適用於 @Embedded 類。

  • 如果要覆蓋這些列定義,請使用 @AttributeOverride 。

  • @Embeddable 類 EmploymentPeriod可以使用指定的屬性覆蓋嵌入到實體類中。

@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 批註指定一個由實體擁有的可嵌入複合主鍵類(通常由兩個或更多基元類型或 JDK 對象類型組成)。從原有數據庫映射時(此時數據庫鍵由多列組成),通常將出現複合主鍵。

複合主鍵類具有下列特徵:

  • 它是一個普通的舊式 Java 對象 (POJO) 類。
  • 它必須為 public ,並且必須有一個 public 無參數構造函數。
  • 如果使用基於屬性的訪問,則主鍵類的屬性必須為 public 或 protected 。
  • 它必須是可序列化的。
  • 它必須定義 equals 和 hashCode 方法。
  • 這些方法的值相等性的語義必須與鍵映射到的數據庫類型的數據庫相等性一致。
@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 批註指定一個實體類從中繼承持久字段的超類。當多個實體類共享通用的持久字段或屬性時,這將是一個方便的模式。

  • 您可以像對實體那樣使用任何直接和關係映射批註(如 @Basic 和 @ManyToMany)對該超類的字段和屬性進行批註,但由於沒有針對該超類本身的表存在,因此這些映射只適用於它的子類。繼承的持久字段或屬性屬於子類的表。

  • 可以在子類中使用 @AttributeOverride 或 @AssociationOverride 批註來覆蓋超類的映射配置。

如何使用此批註將 Employee 指定為映射超類。如何擴展實體中的此超類,以及如何在實體類中使用 @AttributeOverride 以覆蓋超類中設置的配置。

@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將一個或多個實體監聽程序類與 @Entity 或 @MappedSuperclass 關聯,條件是您需要在指定的生命週期事件發生時執行邏輯。

  • 不希望在實體 API 中公開生命週期監聽程序方法。
  • 要在不同的實體類型之間共享生命週期監聽程序邏輯。
  • 當實體或子類上發生生命週期事件時,JPA 持續性提供程序將按監聽程序定義的順序通知每個實體監聽程序,並調用使用相應的生命週期事件類型進行批註的實體監聽程序方法(如果有)。

實體監聽程序類具有以下特徵:

  • 它是一個普通的舊式 Java 對象 (POJO) 類
  • 它有一個或多個具有以下簽名的回調方法:
  • public void <MethodName>(Object)
  • 可以指定參數類型 Object ,或實體監聽程序將與其關聯的實體類的類型。
  • 它用一個或多個生命週期事件批註對每個回調方法進行批註。
  • 一個生命週期事件只能與一個回調監聽程序方法關聯,但某個給定的回調監聽程序方法可以與多個生命週期事件關聯。
  • 如果使用實體監聽程序,則可以管理哪些實體監聽程序使用

@EntityListeners 屬性

顯示了您可以將多個生命週期事件與給定的實體監聽程序類方法關聯,但任何給定的生命週期事件只能在實體監聽程序類中出現一次。

@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

如果默認監聽程序行為不適用,請使用 @ExcludeDefaultListeners 批註覆蓋(並阻止)針對給定 @Entity 或 @MappedSuperclass 執行的默認監聽程序。

	@Entity
	@ExcludeDefaultListeners
	public class Employee implements Serializable {
    ...
} 
「其他文章」