【Java技术指南】「JPA编程专题」让你不再对JPA技术中的“持久化型注解”感到陌生了(一)https://developer.aliyun.com/article/1471007
@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 { ... }