Java技術(shù)指南「JPA編程專(zhuān)題」讓你不再對(duì)JPA技術(shù)中的“持久化型注解”感到陌生了!

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注解總覽


image

image

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 。

image
@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 屬性
image

image

顯示了如何使用此注解為名為 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 一起使用。


image

示例,顯示了如何使用此批注指定 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 屬性

image

image

image

如何使用此批注使 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)
image

顯示了如何使用此注解對(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 屬性

image

顯示了您可以將多個(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 {    ...} 
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容