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

語言: CN / TW / HK

theme: smartblue

小知識,大挑戰!本文正在參與“程式設計師必備小知識”創作活動。

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 。

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

@TableGenerator

  • 如果使用 @GeneratedValue 批註指定一個 TABLE 型別的主鍵生成器,可以使用 @TableGenerator 批註微調該主鍵生成器以:
    • 由於名稱難於處理、是一個保留字、與預先存在的資料模型不相容或作為資料庫中的表名無效而更改主鍵生成器的表名稱
    • 更改分配大小以匹配應用程式要求或資料庫效能引數
    • 更改初始值以匹配現有的資料模型(例如,如果基於已經為其分配或保留了一組主鍵值的現有資料集構建)
    • 使用特定目錄或模式配置主鍵生成器的表
    • 在主鍵生成器表的一列或多列商配置一個唯一的約束
@TableGenerator 屬性

顯示瞭如何使用此註解為名為 empGen 的 TABLE 主鍵生成器指定分配大小。

java @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 ) 資料庫型別。

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

@Transient

  • 預設情況下,JPA 持久化提供程式假設實體的所有欄位均為持久欄位。
  • 使用 @Transient 註解指定實體的非持久欄位或屬性,例如,一個在執行時使用但並非實體狀態一部分的欄位或屬性。
  • JPA 提供程式不會對批註為 @Transient 的屬性或欄位持久儲存(或建立資料庫模式)。
  • 該註解可以與 @Entity 、@MappedSuperclass 和 @Embeddable 一起使用。
  • 該註解沒有屬性。

java @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

```java @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。 java @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

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

@Embeddable 和 @Embedded

  • 使用 @Embeddable 批註指定一個類,該類的例項儲存為擁有實體的固有部分並共享該實體的身份。嵌入物件的每個持久屬性或欄位都將對映到實體的資料庫表。

  • 類 EmploymentPeriod 在用作為 @Embedded 的持久欄位的型別時可以巢狀到實體中.

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

@Embedded

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

  • 嵌入物件的每個持久屬性或欄位均對映到擁有實體的資料庫表。

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

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

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

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

java @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 方法。 - 這些方法的值相等性的語義必須與鍵對映到的資料庫型別的資料庫相等性一致。

```java @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 以覆蓋超類中設定的配置。

```java @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 (Object) - 可以指定引數型別 Object ,或實體監聽程式將與其關聯的實體類的型別。 - 它用一個或多個生命週期事件批註對每個回撥方法進行批註。 - 一個生命週期事件只能與一個回撥監聽程式方法關聯,但某個給定的回撥監聽程式方法可以與多個生命週期事件關聯。 - 如果使用實體監聽程式,則可以管理哪些實體監聽程式使用

@EntityListeners 屬性

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

```java @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 執行的預設監聽程式。

java @Entity @ExcludeDefaultListeners public class Employee implements Serializable {
    ...
}

「其他文章」