您可以有选择地使用批注来修饰实体类以覆盖默认值。这称作按异常进行配置 (configuration by exception)。
本参考广泛引用了 JSR-220 Enterprise JavaBean 版本 3.0 Java 持续性 API 规范,以按类别汇总批注信息(请参阅表 1-1),并解释了何时以及如何使用这些批注来自定义 JPA 行为,以满足应用程序的需要。
表 1-1 按类别划分的 JPA 批注
@AssociationOverride
默认情况下,JPA 持续性提供程序自动假设子类继承超类中定义的持久属性及其关联映射。
如果继承的列定义对实体不正确(例如,如果继承的列名与已经存在的数据模型不兼容或作为数据库中的列名无效),请使用 @AssociationOverride
批注自定义从@MappedSuperclass 或 @Embeddable 继承的 @OneToOne 或 @ManyToOne 映射,以更改与字段或属性关联的 @JoinColumn。
如果有多个要进行的 @AssociationOverride
更改,则必须使用 @AssociationOverrides。
要自定义基本映射以更改它的 @Column,请使用 @AttributeOverride。
表 1-4 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-4 显示了示例 1-5 中的实体扩展的 @MappedSuperclass。示例 1-5 显示了如何在实体子类中使用 @AssociationOverride
覆盖 @MappedSuperclass Employee
中定义(默认情况下)的 @JoinColumn 以便关联到 Address
。
如果使用 @AssociationOverride
,则 Employee
表包含以下列:
如果不使用 @AssociationOverride
,则 Employee
表包含以下列:
示例 1-1 @MappedSuperclass
@MappedSuperclass
public class Employee {
@Id protected Integer id;
@Version protected Integer version;
@ManyToOne protected Address address;
...
}
示例 1-2 @AssociationOverride
@Entity@AssociationOverride(name="address", joinColumns=@JoinColumn(name="ADDR_ID"))public class PartTimeEmployee extends Employee { @Column(name="WAGE") protected Float hourlyWage;
...
}
@AssociationOverrides
如果需要指定多个 @AssociationOverride,则必需使用一个 @AssociationOverrides
批注指定所有关联覆盖。
表 1-5 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-6 显示了如何使用此批注指定两个关联覆盖。
示例 1-3 @AssociationOverrides
@Entity
@AssociationOverrides({
@AssociationOverride(name="address", joinColumn=@Column(name="ADDR_ID")),
@AssociationOverride(name="id", joinColumn=@Column(name="PTID"))
})
public class PartTimeEmployee extends Employee {
@Column(name="WAGE")
protected Float hourlyWage;
...
}
@AttributeOverride
默认情况下,JPA 持续性提供程序自动假设子类继承超类中定义的持久属性及其基本映射。
如果针对实体继承的列定义不正确,请使用 @AttributeOverride
批注自定义一个从 @MappedSuperclass 或 @Embeddable 继承的基本映射以更改与字段或属性关联的@Column。(例如,如果继承的列名与事先存在的数据模型不兼容,或者作为数据库中的列名无效)。
如果有多个要进行的 @AttributeOverride
更改,则必须使用 @AttributeOverrides。
要自定义关联映射以更改它的 @JoinColumn,请使用 @AssociationOverride。
表 1-4 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-4 显示了示例 1-5 中的实体扩展的 @MappedSuperclass。示例 1-5 显示了如何使用实体子类中的 @AttributeOverride
覆盖 @MappedSuperclass Employee
中定义(默认情况下)的 @Column,以便基本映射到 Address
。
如果使用 @AttributeOverride
,则 Employee
表包含以下列:
-
ID
-
VERSION
-
ADDR
_STRING
-
WAGE
如果不使用 @AttributeOverride
,则 Employee
表包含以下列:
示例 1-4 @MappedSuperclass
@MappedSuperclass
public class Employee {
@Id protected Integer id;
@Version protected Integer version;
protected String address;
...
}
示例 1-5 @AttributeOverride
@Entity
@AttributeOverride(name="address", column=@Column(name="ADDR_STRING"))
public class PartTimeEmployee extends Employee {
@Column(name="WAGE")
protected Float hourlyWage;
...
}
@AttributeOverrides
如果需要指定多个 @AttributeOverride,则必需使用一个 @AttributeOverrides
批注指定所有属性覆盖。
表 1-5 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-6 显示了如何使用此批注指定两个属性覆盖。
示例 1-6 @AttributeOverrides
@Entity
@AttributeOverrides({
@AttributeOverride(name="address", column=@Column(name="ADDR_ID")),
@AttributeOverride(name="id", column=@Column(name="PTID"))
})
public class PartTimeEmployee extends Employee {
@Column(name="WAGE")
protected Float hourlyWage;
public PartTimeEmployee() {
...
}
public Float getHourlyWage() {
...
}
public void setHourlyWage(Float wage) {
...
}
}
@Basic
默认情况下,JPA 持续性提供程序为大多数 Java 基元类型、基元类型的包装程序以及枚举自动配置一个 @Basic
映射。
使用 @Basic
批注:
表 1-6 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-7 显示了如何使用此批注为基本映射指定获取类型 LAZY
。
示例 1-7 @Basic
@Entity
public class Employee implements Serializable {
...
@Basic(fetch=LAZY)
protected String getName() {
return name;
}
...
}
@Column
默认情况下,JPA 持续性提供程序假设每个实体的持久字段存储在其名称与持久字段的名称相匹配的数据库表列中。
使用 @Column
批注:
表 1-7 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-8 显示了如何使用此批注使 JPA 将 empId
持久保存到辅助表 EMP_HR
中的列 EMP_NUM
。默认情况下,JPA 将 empName
持久保存到主表 Employee
中的列 empName
。
示例 1-8 @Column
@Entity
@SecondaryTable(name="EMP_HR")
public class Employee implements Serializable {
...
@Column(name="EMP_NUM", table="EMP_HR")
private Long empId;
private String empName;
...
}
@ColumnResult
执行 @NamedNativeQuery 时,它可以返回实体(包括不同类型的实体)、标量值或实体和标量值的组合。
使用 @ColumnResult
批注返回标量值。标量类型由您在 @ColumnResult
中标识的列类型确定。
有关详细信息,另请参阅 @EntityResult、@FieldResult 和 @SqlResultSetMapping。
表 1-8 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-9 显示了如何使用此批注将 Item
(请参阅示例 1-10)标量 name
包含在结果列表(请参阅示例 1-11)中。在该示例中,结果列表将为 Object
数组的 List
,如:{[Order, "Shoes"], [Order, "Socks"], ...}
。
示例 1-9 使用 @ColumnResult 的 Order 实体
@SqlResultSetMapping(
name="OrderResults",
entities={
@EntityResult(
entityClass=Order.class,
fields={
@FieldResult(name="id", column="order_id"),
@FieldResult(name="quantity", column="order_quantity"),
@FieldResult(name="item", column="order_item")
}
)
},
columns={
@ColumnResult(
name="item_name"
)
}
)
@Entity
public class Order {
@Id
protected int id;
protected long quantity;
protected Item item;
...
}
示例 1-10 Item 实体
@Entity
public class Item {
@Id
protected int id;
protected String name;
...
}
示例 1-11 结合使用 @SqlResultSetMapping 与 @ColumnResult 的原生查询
Query q = entityManager.createNativeQuery(
"SELECT o.id AS order_id, " +
"o.quantity AS order_quantity, " +
"o.item AS order_item, " +
"i.name AS item_name, " +
"FROM Order o, Item i " +
"WHERE (order_quantity > 25) AND (order_item = i.id)",
"OrderResults"
);
List resultList = q.getResultList();
// List of Object arrays:{[Order, "Shoes"], [Order, "Socks"], ...}
@DiscriminatorColumn
默认情况下,当 @Inheritance 属性策略为 InheritanceType.SINGLE_TABLE
或 JOINED
时,JPA 持续性提供程序将创建一个名为 DTYPE
的标识符列以区分继承层次中的类。
使用 @DiscriminatorColumn
批注:
表 1-9 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-12 显示了如何使用此批注指定一个名为 DISC
、类型为 STRING
、长度为 20 的标识符列。在本示例中,该类的 @DiscriminatorValue 指定为 CUST
。示例 1-13 中的子类将它自己的 @DiscriminatorValue 指定为 VIP
。在 Customer
和 ValuedCustomer
中,@DiscriminatorValue 的值必须可以转换为由 @DiscriminatorColumn 属性 discriminatorType
指定的类型,并且必须符合 @DiscriminatorColumn 属性 length
。
示例 1-12 @DiscriminatorColumn 和 @DiscriminatorValue — 根类
@Entity
@Table(name="CUST")
@Inheritance(strategy=SINGLE_TABLE)
@DiscriminatorColumn(name="DISC", discriminatorType=STRING, length=20)
@DiscriminatorValue(value-"CUST")
public class Customer {
...
}
示例 1-13 @DiscriminatorValue — 子类
@Entity
@DiscriminatorValue(value="VIP")
public class ValuedCustomer extends Customer {
...
}
@DiscriminatorValue
默认情况下,当 @Inheritance 属性策略为 InheritanceType.SINGLE_TABLE
或 JOINED
时,JPA 持续性提供程序使用 @DiscriminatorColumn 按实体名称区分继承层次中的类(请参阅 @Entity)。
使用 @DiscriminatorValue
批注指定用于区分此继承层次中的实体的标识符值:
-
如果实体名称不适合于此应用程序
-
匹配现有的数据库模式
表 1-10 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-14 显示了如何使用此批注指定一个名为 DISC
、类型为 STRING
、长度为 20 的标识符列。在本示例中,该类的 @DiscriminatorValue 指定为 CUST
。示例 1-15 中的子类将它自己的 @DiscriminatorValue 指定为 VIP
。在 Customer
和 ValuedCustomer
中,@DiscriminatorValue 的值必须可以转换为由 @DiscriminatorColumn 属性 discriminatorType
指定的类型,并且必须符合 @DiscriminatorColumn 属性 length
。
示例 1-14 @DiscriminatorColumn 和 @DiscriminatorValue — 根类
@Entity
@Table(name="CUST")
@Inheritance(strategy=SINGLE_TABLE)
@DiscriminatorColumn(name="DISC", discriminatorType=STRING, length=20)
@DiscriminatorValue(value-"CUST")
public class Customer {
...
}
示例 1-15 @DiscriminatorValue — 子类
@Entity
@DiscriminatorValue(value="VIP")
public class ValuedCustomer extends Customer {
...
}
@Embeddable
默认情况下,JPA 持续性提供程序假设每个实体均持久保存到它自己的数据库表。
使用 @Embeddable
批注指定一个类,该类的实例存储为拥有实体的固有部分并共享该实体的身份。嵌入对象的每个持久属性或字段都将映射到实体的数据库表。
此批注没有属性
。有关更多详细信息,请参阅 API。
示例 1-16 显示了如何使用此批注指定:类 EmploymentPeriod
在用作批注为 @Embedded 的持久字段的类型时可以嵌套到实体中(请参阅示例 1-17)
示例 1-16 @Embeddable
@Embeddable
public class EmploymentPeriod {
java.util.Date startDate;
java.util.Date endDate;
...
}
@Embedded
默认情况下,JPA 持续性提供程序假设每个实体均持久保存到它自己的数据库表。
使用 @Embedded
批注指定一个持久字段,该字段的 @Embeddable 类型可以存储为拥有实体的固有部分,并共享该实体的身份。嵌入对象的每个持久属性或字段均映射到拥有实体的数据库表。
可以结合使用 @Embedded
和 @Embeddable 以建立严格所有权关系的模型,以便在删除了拥有对象的情况下还将删除被拥有的对象。
嵌入的对象不应映射到多个表。
默认情况下,@Embeddable 类中指定的列定义(请参阅 @Column)适用于 @Embedded
类。如果要覆盖这些列定义,请使用 @AttributeOverride。
此批注没有属性。有关更多详细信息,请参阅 API。
示例 1-17 显示了如何使用该批注指定:@Embeddable 类 EmploymentPeriod
(请参阅示例 1-16)可以使用指定的属性覆盖(请参阅 @AttributeOverride)嵌入到实体类中。如果不需要属性覆盖,则可以完全忽略 @Embedded
批注:JPA 持续性提供程序将推断出 EmploymentPeriod
是从它的 @Embeddable 批注进行嵌套。
示例 1-17 @Embedded
@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
方法。
这些方法的值相等性的语义必须与键映射到的数据库类型的数据库相等性一致。
或者,您可以使复合主键类成为非嵌入类(请参阅 @IdClass)。
此批注没有属性
。有关更多详细信息,请参阅 API。
示例 1-18 显示了一个批注为 @Embeddable 的典型复合主键类。示例1-19 显示了如何使用可嵌入的复合主键类(使用 @EmbeddedId
批注)配置一个实体。
示例 1-18 可嵌入复合主键类
@Embeddable
public class EmployeePK implements Serializable
{
private String name;
private long id;
public EmployeePK()
{
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
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);
}
}
示例 1-19 @EmbeddedId
@Entity
public class Employee implements Serializable
{
EmployeePK primaryKey;
public Employee()
{
}
@EmbeddedId
public EmployeePK getPrimaryKey()
{
return primaryKey;
}
public void setPrimaryKey(EmployeePK pk)
{
primaryKey = pk;
}
...
}
@Entity
使用 @Entity
批注将普通的旧式 Java 对象 (POJO) 类指定为实体,并使其可用于 JPA 服务。必须将 POJO 类指定为实体,然后才可以使用任何其他 JPA 批注。
表 1-11 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-20 显示了该批注的用法。
示例 1-20 @Entity
@Entity
public class Employee implements Serializable {
...
}
@EntityListeners
可以使用生命周期批注(请参阅生命周期事件批注)指定实体中的方法,这些方法在指定的生命周期事件发生时执行您的逻辑。
使用 @EntityListeners
批注将一个或多个实体监听程序类与 @Entity 或 @MappedSuperclass 关联,条件是您需要在指定的生命周期事件发生时执行逻辑,以及:
当实体或子类上发生生命周期事件时,JPA 持续性提供程序将按监听程序定义的顺序通知每个实体监听程序,并调用使用相应的生命周期事件类型进行批注的实体监听程序方法(如果有)。
实体监听程序类具有以下特征:
-
它是一个普通的旧式 Java 对象 (POJO) 类
-
它有一个或多个具有以下签名的回调方法:
public void <MethodName>(Object)
可以指定参数类型 Object
,或实体监听程序将与其关联的实体类的类型。
-
它用一个或多个生命周期事件批注对每个回调方法进行批注。
一个生命周期事件只能与一个回调监听程序方法关联,但某个给定的回调监听程序方法可以与多个生命周期事件关联。
如果使用实体监听程序,则可以管理哪些实体监听程序使用 @ExcludeDefaultListeners 和 @ExcludeSuperclassListeners 调用。
表 1-12 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-21 显示了如何使用此批注将实体监听程序类 EmployeePersistListener
(请参阅示例 1-22)和 EmployeeRemoveListener
(请参阅示例 1-23)与实体 Employee
关联。示例 1-23 显示了您可以将多个生命周期事件与给定的实体监听程序类方法关联,但任何给定的生命周期事件只能在实体监听程序类中出现一次。
示例 1-21 @EntityListeners
@Entity
@EntityListeners(value={EmployeePersistListner.class, EmployeeRemoveListener.class})
public class Employee implements Serializable {
...
}
示例 1-22 EmployeePersistListener
public class EmployeePersistListener {
@PrePersist
employeePrePersist(Object employee) {
...
}
...
}
示例 1-23 EmployeeRemoveListener
public class EmployeeRemoveListener {
@PreRemove
@PostRemove
employeePreRemove(Object employee) {
...
}
...
}
@EntityResult
执行 @NamedNativeQuery 时,它可以返回实体(包括不同类型的实体)、标量值或实体和标量值的组合。
使用 @EntityResult
批注返回实体。
有关详细信息,另请参阅 @ColumnResult、@FieldResult 和 @SqlResultSetMapping。
表 1-8 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-24 显示了如何使用此批注将 Order
和 Item
(请参阅示例 1-25)实体包含在结果列表(请参阅示例 1-26)中。在该示例中,结果列表将为 Object
数组的 List
,如:{[Order, Item], [Order, Item], ...}
。
示例 1-24 使用 @EntityResult 的 Order 实体
@SqlResultSetMapping(
name="OrderResults",
entities={
@EntityResult(
entityClass=Order.class,
fields={
@FieldResult(name="id", column="order_id"),
@FieldResult(name="quantity", column="order_quantity"),
@FieldResult(name="item", column="order_item")
}
),
@EntityResult(
entityClass=Item.class,
fields={
@FieldResult(name="id", column="item_id"),
@FieldResult(name="name", column="item_name"),
}
)
}
)
@Entity
public class Order {
@Id
protected int id;
protected long quantity;
protected Item item;
...
}
示例 1-25 Item 实体
@Entity
public class Item {
@Id
protected int id;
protected String name;
...
}
示例 1-26 结合使用 @SqlResultSetMapping 与 @EntityResult 的原生查询
Query q = entityManager.createNativeQuery(
"SELECT o.id AS order_id, " +
"o.quantity AS order_quantity, " +
"o.item AS order_item, " +
"i.id AS item_id, " +
"i.name AS item_name, " +
"FROM Order o, Item i " +
"WHERE (order_quantity > 25) AND (order_item = i.id)",
"OrderResults"
);
List resultList = q.getResultList();
// List of Object arrays:{[Order, Item], [Order, Item], ...}
@Enumerated
默认情况下,JPA 持续性提供程序持久保存枚举常量的序数值。
使用 @Enumerated
批注指定在 String 值适合应用程序要求或与现有数据库模式匹配的情况下,JPA 持续性提供程序是否应持久保存枚举常量的序数值或 String
值。
该批注可以与 @Basic 一起使用。
表 1-14 列出了此批注的属性
。有关更多详细信息,请参阅 API。
根据示例 1-27 中的枚举常量,示例 1-28 显示了如何使用此批注指定在持久保存 Employee
时应将 SalaryRate
的 String
值写入数据库。默认情况下,会将 EmployeeStatus
的序数值写入数据库。
示例 1-27 枚举常量
public enum EmployeeStatus {FULL_TIME, PART_TIME, CONTRACT}
public enum SalaryRate {JUNIOR, SENIOR, MANAGER, EXECUTIVE}
示例 1-28 @Enumerated
@Entity
public class Employee {
...
public EmployeeStatus getStatus() {
...
}
@Enumerated(STRING)
public SalaryRate getPayScale() {
...
}
...
}
@ExcludeDefaultListeners
默认监听程序是 orm.xml
文件中指定的一个生命周期事件监听程序类,该类应用于持续性单元(请参阅 @PersistenceUnit)中的所有实体。在调用任何其他实体监听程序(请参阅 @EntityListeners)之前,JPA 持续性提供程序首先按照 orm.xml
文件中定义的顺序调用默认监听程序(如果有)。
如果默认监听程序行为不适用,请使用 @ExcludeDefaultListeners
批注覆盖(并阻止)针对给定 @Entity 或 @MappedSuperclass 执行的默认监听程序。
此批注没有属性
。有关更多详细信息,请参阅 API。
示例 1-29 显示了如何使用此批注指定不应对 Employee
实体执行默认监听程序。
示例 1-29 @ExcludeDefaultListeners
@Entity
@ExcludeDefaultListeners
public class Employee implements Serializable {
...
}
@ExcludeSuperclassListeners
如果继承层次中的 @Entity 和 @MappedSuperclass 类定义了 @EntityListeners,则默认情况下,JPA 持续性提供程序将在调用子类监听程序之前调用超类监听程序。
如果超类监听程序行为不适用,则使用 @ExcludeSuperclassListeners
批注覆盖(并阻止)针对给定 @Entity 或 @MappedSuperclass 执行的超类监听程序。
@ExcludeSuperclassListeners
批注不影响默认监听程序(请参阅 @ExcludeDefaultListeners)。
此批注没有属性
。有关更多详细信息,请参阅 API。
示例 1-29 显示了如何使用此批注指定不应对 PartTimeEmployee
实体执行超类监听程序 EmployeeListener
,而是执行默认监听程序和子类监听程序 PartTimeEmployeeListener1
和 PartTimeEmployeeListener2
。
示例 1-30 超类级别的实体监听程序
@MappedSuperclass
@EntityListeners(value={EmployeeListener.class})
public class Employee {
...
}
示例 1-31 子类级别的 @ExcludeSuperclassListeners
@Entity
@ExcludeSuperclassListeners
@EntityListners(value={PartTimeEmployeeListener1.class, PartTimeEmployeeListener2.class})
public class PartTimeEmployee extends Employee {
...
}
@FieldResult
执行 @NamedNativeQuery 时,它可以返回实体(包括不同类型的实体)、标量值或实体和标量值的组合。
默认情况下,JPA 持续性提供程序假设在使用 @EntityResult 返回实体时,SELECT
语句将包含与返回的实体的所有字段或属性相对应的所有列,且 SELECT 语句中的列名对应于字段或属性名(未使用 AS 语句)。
如果 SELECT
语句只包含某些与返回的实体的字段或属性相对应的列,或 SELECT
语句中的列名并不对应于字段或属性名(使用了 AS
语句),则在使用 @EntityResult 返回实体时,请使用 @FieldResult
批注将 SELECT
语句中的列映射到字段或属性。
有关详细信息,另请参阅 @ColumnResult 和 @SqlResultSetMapping。
表 1-15 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-32 显示了如何使用此批注将 Order
和 Item
(请参阅示例 1-33)实体包含在结果列表(请参阅示例 1-34)中。在该示例中,结果列表将为 Object
数组的 List
,如:{[Order, Item], [Order, Item], ...}
。
示例 1-32 使用 @EntityResult 和 @FieldResult 的 Order 实体
@SqlResultSetMapping(
name="OrderResults",
entities={
@EntityResult(
entityClass=Order.class,
fields={
@FieldResult(name="id", column="order_id"),
@FieldResult(name="quantity", column="order_quantity"),
@FieldResult(name="item", column="order_item")
}
),
@EntityResult(
entityClass=Item.class,
fields={
@FieldResult(name="id", column="item_id"),
@FieldResult(name="name", column="item_name"),
}
)
}
)
@Entity
public class Order {
@Id
protected int id;
protected long quantity;
protected Item item;
...
}
示例 1-33 Item 实体
@Entity
public class Item {
@Id
protected int id;
protected String name;
...
}
示例 1-34 结合使用 @SqlResultSetMapping 与 @EntityResult 的原生查询
Query q = entityManager.createNativeQuery(
"SELECT o.id AS order_id, " +
"o.quantity AS order_quantity, " +
"o.item AS order_item, " +
"i.id AS item_id, " +
"i.name AS item_name, " +
"FROM Order o, Item i " +
"WHERE (order_quantity > 25) AND (order_item = i.id)",
"OrderResults"
);
List resultList = q.getResultList();
// List of Object arrays:{[Order, Item], [Order, Item], ...}
@GeneratedValue
默认情况下,JPA 持续性提供程序管理为实体主键提供的唯一标识符(请参阅 @Id)。
如果要微调此机制以实现以下目的,请使用 @GeneratedValue
批注:
表 1-16 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-35 显示了如何使用此批注指示持续性提供程序使用名为 CUST_SEQ
、类型为 GeneratorType.SEQUENCE
的主键生成器。
示例 1-35 @GeneratedValue
@Entity
public class Employee implements Serializable {
...
@Id
@GeneratedValue(strategy=SEQUENCE, generator="CUST_SEQ")
@Column(name="CUST_ID")
public Long getId() {
return id;
}
...
}
@Id
使用 @Id
批注将一个或多个持久字段或属性指定为实体的主键。
对于每个实体,必须至少指定以下项之一:
此批注没有属性
。有关更多详细信息,请参阅 API。
默认情况下,JPA 持续性提供程序选择最合适的主键生成器(请参阅 @GeneratedValue)并负责管理主键值:您不必采取任何进一步的操作。如果要使用 JPA 持续性提供程序的默认键生成机制,则不必采取任何进一步的操作。
示例 1-36 显示了如何使用此批注将持久字段 empID
指定为 Employee
表的主键。
示例 1-36 @Id
@Entity
public class Employee implements Serializable {
@Id
private int empID;
...
}
@IdClass
使用 @IdClass
批注为实体指定一个复合主键类(通常由两个或更多基元类型或 JDK 对象类型组成)。从原有数据库映射时(此时数据库键由多列组成),通常将出现复合主键。
复合主键类具有下列特征:
-
它是一个普通的旧式 Java 对象 (POJO) 类。
-
它必须为 public,并且必须有一个 public 无参数构造函数。
-
如果使用基于属性的访问,则主键类的属性必须为 public 或 protected。
-
它必须是可序列化的。
-
它必须定义 equals
和 hashCode
方法。
这些方法的值相等性的语义必须与键映射到的数据库类型的数据库相等性一致。
-
它的字段或属性的类型和名称必须与使用 @Id 进行批注的实体主键字段或属性的类型和名称相对应。
或者,您可以使复合主键类成为由实体拥有的嵌入类(请参阅 @EmbeddedId)。
表 1-17 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-37 显示了一个非嵌入的复合主键类。在该类中,字段 empName
和 birthDay
的名称和类型必须对应于实体类中属性的名称和类型。示例 1-38 显示了如何使用这个非嵌入的复合主键类(使用 @IdClass
批注)配置 EJB 3.0 实体。由于实体类字段 empName
和 birthDay
在主键中使用,因此还必须使用 @Id 批注对其进行批注。
示例 1-37 非嵌入的复合主键类
public class EmployeePK implements Serializable
{
private String empName;
private Date birthDay;
public EmployeePK()
{
}
public String getName()
{
return empName;
}
public void setName(String name)
{
empName = name;
}
public long getDateOfBirth()
{
return birthDay;
}
public void setDateOfBirth(Date date)
{
birthDay = date;
}
public int hashCode()
{
return (int) empName.hashCode();
}
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.birthDay == birthDay && pk.empName.equals(empName);
}
}
示例 1-38 @IdClass
@IdClass(EmployeePK.class)
@Entity
public class Employee
{
@Id String empName;
@Id Date birthDay;
...
}
@Inheritance
默认情况下,JPA 持续性提供程序自动管理继承层次中实体的持续性。
使用 @Inheritance
批注自定义持续性提供程序的继承层次支持,以提高应用程序性能或匹配现有的数据模型。
表 1-18 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-39 显示了如何使用此批注指定 Customer
的所有子类将使用 InheritanceType.JOINED
。示例 1-40 中的子类将映射到它自己的表(该表针对 ValuedCustomer
的每个持久属性包含一列)和一个外键列(包含 Customer
表的主键)。
示例 1-39 @Inheritance — 使用 JOINED 的根类
@Entity@Inheritance(strategy=JOINED)public class Customer {
@Id
private int customerId;
...
}
示例 1-40 @Inheritance — 使用 JOINED 的子类
@Entity
public class ValuedCustomer extends Customer {
...
}
在示例 1-41 中,默认情况下,InheritanceType.SINGLE_TABLE
应用于 Customer
及其所有子类。在该示例中,默认标识符表列 DTYPE
(请参阅 @DiscriminatorColumn)指定为具有标识符类型 INTEGER
,且 Customer
的 @DiscriminatorValue 指定为 1
。示例 1-42 显示了如何将子类 ValuedCustomer
的标识符值指定为 2
。在该示例中,Customer
和ValuedCustomer
的所有持久属性将映射到一个表。
示例 1-41 @Inheritance — 指定其标识符列的根类
@Entity
@DiscriminatorColumn(discriminatorType=DiscriminatorType.INTEGER)
@DiscriminatorValue(value="1")
public class Customer {
...
}
示例 1-42 @Inheritance — 指定其标识符值的子类
@Entity
@DiscriminatorValue(value="2")
public class ValuedCustomer extends Customer {
...
}
@JoinColumn
默认情况下,在实体关联中,JPA 持续性提供程序使用一个基于现有名称(如字段或属性名称)的数据库模式,以便它可以自动确定要使用的单个连接列(包含外键的列)。
在以下条件下使用 @JoinColumn
批注:
-
默认连接列名称难于处理、是一个保留字、与预先存在的数据模型不兼容或作为数据库中的列名无效
-
您需要使用外部表中的列(非主键列)进行连接
-
您想要使用两个或更多连接列(请参阅 @JoinColumns)
-
您想要使用一个连接表(请参阅 @JoinTable)
表 1-19 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-43 显示了如何使用此批注使 JPA 将数据库表 Employee
列 ADDR_ID
用作连接列。
示例 1-43 @JoinColumn
@Entity
public class Employee implements Serializable {
...
@ManyToOne
@JoinColumn(name="ADDR_ID")
public Address getAddress() {
return address;
}
}
@JoinColumns
默认情况下,在实体关联中,JPA 持续性提供程序假设使用一个连接列。
如果要指定两个或更多连接列(即复合主键),请使用 @JoinColumns
批注。
表 1-20 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-44 显示了如何使用此批注指定两个连接列的名称:Employee
表中的 ADDR_ID
(其中包含 Address
表列 ID
中的外键值)以及 Employee
表中的 ADDR_ZIP
(其中包含Address
表列 ZIP
中的外键值)。
示例 1-44 @JoinColumns
@Entity
public class Employee implements Serializable {
...
@ManyToOne
@JoinColumns({
@JoinColumn(name="ADDR_ID", referencedColumnName="ID"),
@JoinColumn(name="ADDR_ZIP", referencedColumnName="ZIP")
})
public Address getAddress() {
return address;
}
...
}
@JoinTable
默认情况下,JPA 持续性提供程序在映射多对多关联(或在单向的一对多关联中)的拥有方上的实体关联时使用一个连接表。连接表名称及其列名均在默认情况下指定,且 JPA 持续性提供程序假设:在关系的拥有方上的实体主表中,每个主键列有一个连接列。
如果您需要执行以下操作,请使用 @JoinTable
批注:
表 1-21 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-45 显示了如何使用此批注为 Employee
与 Project
之间实体的多对多关系指定一个名为 EMP_PROJ_EMP
的连接表。连接表中有两列:EMP_ID
和 PROJ_ID
。EMP_ID
列包含其主键列(被引用列)名为 ID
的 Employee
表中的主键值。PROJ_ID
列包含其主键列(被引用列)也名为 ID
的 Project
表中的主键值。
示例 1-45 @JoinTable
@Entity
public class Employee implements Serializable {
...
@ManyToMany
@JoinTable(
name="EJB_PROJ_EMP",
joinColumns=@JoinColumn(name="EMP_ID", referencedColumnName="ID"),
inverseJoinColumns=@JoinColumn(name="PROJ_ID", referencedColumnName="ID")
)
public Collection getProjects() {
return projects;
}
...
}
@Lob
默认情况下,JPA 持续性提供程序假设所有持久数据均可以表示为典型的数据库数据类型。
结合使用 @Lob
批注与 @Basic 映射,以指定持久属性或字段应作为大型对象持久保存到数据库支持的大型对象类型。
Lob 可以是二进制类型或字符类型。持续性提供程序从持久字段或属性的类型推断出 Lob 类型。
对于基于字符串和字符的类型,默认值为 Clob。在所有其他情况下,默认值为 Blob。
还可以使用 @Column 属性 columnDefinition
进一步改进 Lob
类型。
此批注没有属性
。有关更多详细信息,请参阅 API。
示例 1-46 显示了如何使用此批注指定持久字段 pic
应作为 Blob
进行持久保存。
示例 1-46 @Lob
@Entity
public class Employee implements Serializable {
...
@Lob
@Basic(fetch=LAZY)
@Column(name="EMP_PIC", columnDefinition="BLOB NOT NULL")
protected byte[] pic;
...
}
@ManyToMany
默认情况下,JPA 为具有多对多多重性的为多值关联自动定义一个 @ManyToMany
映射。
使用 @ManyToMany
批注:
-
将获取类型配置为 LAZY
-
如果空值不适合于应用程序,则将映射配置为禁止空值(针对非基元类型)
-
由于所使用的 Collection
不是使用一般参数定义的,因此配置关联的目标实体
-
配置必须层叠到关联目标的操作:例如,如果删除了拥有实体,则确保还删除关联的目标
-
配置由持续性提供程序使用的连接表的详细信息(请参阅 @JoinTable)
表 1-22 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-47 和示例 1-48 显示了如何使用此批注在使用一般参数的 Customer
和 PhoneNumber
之间配置一个多对多映射。
示例 1-47 @ManyToMany — 使用一般参数的 Customer 类
@Entity
public class Customer implements Serializable {
...
@ManyToMany
@JoinTable(
name="CUST_PHONE",
joinColumns=
@JoinColumn(name="CUST_ID", referencedColumnName="ID"),
inverseJoinColumns=
@JoinColumn(name="PHONE_ID", referencedColumnName="ID")
)
public Set<PhoneNumber> getPhones() {
return phones;
}
...
}
示例 1-48 @ManyToMany — 使用一般参数的 PhoneNumber 类
@Entity
public class PhoneNumber implements Serializable {
...
@ManyToMany(mappedBy="phones")
public Set<Customer> getCustomers() {
return customers;
}
...
}
@ManyToOne
默认情况下,JPA 为指向具有多对一多重性的其他实体类的单值关联自动定义一个 ManyToOne
映射。
使用 @ManyToOne
批注:
-
将获取类型配置为 LAZY
-
如果空值不适合于应用程序,则将映射配置为禁止空值(针对非基元类型)
-
配置关联的目标实体(如果无法从被引用的对象类型推断出它)
-
配置必须层叠到关联目标的操作:例如,如果删除了拥有实体,则确保还删除关联的目标
表 1-23 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-49 显示了如何使用此批注在使用一般参数的 Customer
(被拥有方)和 Order
(拥有方)之间配置一个多对一映射。
示例 1-49 @ManyToOne
@Entity
public class Order implements Serializable {
...
@ManyToOne(optional=false)
@JoinColumn(name="CUST_ID", nullable=false, updatable=false)
public Customer getCustomer() {
return customer;
}
...
}
@MapKey
默认情况下,JPA 持续性提供程序假设关联实体的主键为 java.util.Map
类型的关联的 Map
键:
使用 @MapKey
批注:
指定的字段或属性必须具有唯一约束(请参阅 @UniqueConstraint)。
表 1-24 列出了此批注的属性
。有关更多详细信息,请参阅 API。
在示例 1-52 中,Project 对作为 Map
的 Employee
实例拥有一对多关系。示例 1-52 显示了如何使用 @MapKey
批注指定此 Map
的键为 Employee
字段 empPK
,它是一个类型为EmployeePK
(请参阅示例 1-52)的嵌入式复合主键(请参阅示例 1-51)。
示例 1-50 使用 @MapKey 的 Project 实体
@Entitypublic class Project { ...@OneToMany(mappedBy="project") @MapKey(name="empPK") public Map<EmployeePK, Employee> getEmployees() { ...
} ...}
示例 1-51 Employee 实体
@Entitypublic class Employee { @EmbeddedId public EmployeePK getEmpPK() { ...
} ... @ManyToOne @JoinColumn(name="proj_id") public Project getProject() {
...
}...}
示例 1-52 EmployeePK 复合主键类
@Embeddablepublic class EmployeePK { String name; Date birthDate;}
@MappedSuperclass
默认情况下,JPA 持续性提供程序假设实体的所有持久字段均在该实体中定义。
使用 @MappedSuperclass
批注指定一个实体类从中继承持久字段的超类。当多个实体类共享通用的持久字段或属性时,这将是一个方便的模式。
您可以像对实体那样使用任何直接和关系映射批注(如 @Basic 和 @ManyToMany)对该超类的字段和属性进行批注,但由于没有针对该超类本身的表存在,因此这些映射只适用于它的子类。继承的持久字段或属性属于子类的表。
可以在子类中使用 @AttributeOverride 或 @AssociationOverride 批注来覆盖超类的映射配置。
该批注没有属性。有关更多详细信息,请参阅 API。
示例 1-53 显示了如何使用此批注将 Employee
指定为映射超类。示例 1-54 显示了如何扩展实体中的此超类,以及如何在实体类中使用 @AttributeOverride 以覆盖超类中设置的配置。
示例 1-53 @MappedSuperclass
@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) {
...
}
}
示例 1-54 扩展 @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) {
...
}
}
@NamedNativeQueries
如果需要指定多个 @NamedNativeQuery,则必须使用一个 @NamedNativeQueries
批注指定所有命名查询。
表 1-5 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-6 显示了如何使用此批注指定两个命名原生查询。
示例 1-55 @NamedNativeQueries
@Entity
@NamedNativeQueries({
@NamedNativeQuery(
name="findAllPartTimeEmployees",
query="SELECT * FROM EMPLOYEE WHERE PRT_TIME=1"
),
@NamedNativeQuery(
name="findAllSeasonalEmployees",
query="SELECT * FROM EMPLOYEE WHERE SEASON=1"
)
})
public class PartTimeEmployee extends Employee {
...
}
@NamedNativeQuery
在使用 JPA 持续性提供程序的应用程序中,可以使用实体管理器动态创建和执行查询,也可以预定义查询并在运行时按名称执行。
使用 @NamedNativeQuery
批注创建与 @Entity 或 @MappedSuperclass 关联的预定义查询,这些查询:
如果有多个要定义的 @NamedNativeQuery
,则必须使用 @NamedNativeQueries。
要预定义适合于任何数据库的可移植查询,请参阅 @NamedQuery。
表 1-6 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-59 显示了如何使用 @NamedNativeQuery
批注定义一个使用基础数据库的原生 SQL 的查询。示例 1-60 显示了如何使用 EntityManager
获取此查询以及如何通过 Query
方法 getResultList
执行该查询。
示例 1-56 使用 @NamedNativeQuery 实现一个 Oracle 层次查询
@Entity
@NamedNativeQuery(
name="findAllEmployees",
query="SELECT * FROM EMPLOYEE"
)
public class Employee implements Serializable {
...
}
示例 1-57 执行一个命名原生查询
Query queryEmployees = em.createNamedQuery("findAllEmployees");
Collection employees = queryEmployees.getResultList();
@NamedQueries
如果需要指定多个 @NamedQuery,则必须使用一个 @NamedQueries
批注指定所有命名查询。
表 1-5 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-6 显示了如何使用此批注指定两个命名查询。
示例 1-58 @NamedQueries
@Entity
@NamedQueries({
@NamedQuery(
name="findAllEmployeesByFirstName",
query="SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName = :firstname"
),
@NamedQuery(
name="findAllEmployeesByLasttName",
query="SELECT OBJECT(emp) FROM Employee emp WHERE emp.lasstName = :lastname"
)
})
public class PartTimeEmployee extends Employee {
...
}
@NamedQuery
在使用 JPA 持续性提供程序的应用程序中,可以使用实体管理器动态创建和执行查询,也可以预定义查询并在运行时按名称执行。
使用 @NamedQuery
批注创建与 @Entity 或 @MappedSuperclass 关联的预定义查询,这些查询:
如果有多个要定义的 @NamedQuery
,则必须使用 @NamedQueries。
要在已知的基础数据库中预定义原生 SQL 查询,请参阅 @NamedNativeQuery。使用原生 SQL 查询,您可以返回实体(包括不同类型的实体)、标量值或同时返回两者。
表 1-6 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-59 显示了如何使用 @NamedQuery
批注定义一个JPA 查询语言查询,该查询使用名为 firstname
的参数。示例 1-60 显示了如何使用 EntityManager
获取此查询并使用Query
方法 setParameter
设置 firstname
参数。
示例 1-59 使用 @NamedQuery 实现一个带参数的查询
@Entity
@NamedQuery(
name="findAllEmployeesByFirstName",
query="SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName = :firstname"
)
public class Employee implements Serializable {
...
}
示例 1-60 执行命名查询
Query queryEmployeesByFirstName = em.createNamedQuery("findAllEmployeesByFirstName");
queryEmployeeByFirstName.setParameter("firstName", "John");
Collection employees = queryEmployessByFirstName.getResultList();
@OneToMany
默认情况下,JPA 为具有一对多多重性的多值关联定义一个 OneToMany
映射。
使用 @OneToMany
批注:
-
将获取类型配置为 LAZY
-
由于所使用的 Collection
不是使用一般参数定义的,因此配置关联的目标实体
-
配置必须层叠到关联目标的操作:例如,如果删除了拥有实体,则确保还删除关联的目标
-
配置持续性提供程序对单向一对多关系使用的连接表(请参阅 @JoinTable)的详细信息
表 1-29 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-61 和示例 1-62 显示了如何使用此批注在使用一般参数的 Customer
(被拥有方)和 Order
(拥有方)之间配置一个一对多映射。
示例 1-61 @OneToMany - 使用一般参数的 Customer 类
@Entity
public class Customer implements Serializable {
...
@OneToMany(cascade=ALL, mappedBy="customer")
public Set<Order> getOrders() {
return orders;
}
...
}
示例 1-62 @ManyToOne - 使用一般参数的 Order 类
@Entity
public class Customer implements Serializable {
...
@ManyToOne
@JoinColumn(name="CUST_ID", nullable=false)
public Customer getCustomer() {
return customer;
}
...
}
@OneToOne
默认情况下,JPA 为指向另一个具有一对一多重性的实体的单值关联定义一个 OneToOne
映射,并从被引用的对象类型推断出关联的目标实体。
使用 @OneToOne
批注:
-
将获取类型配置为 LAZY
-
如果空值不适合于应用程序,则将映射配置为禁止空值(针对非基元类型)
-
配置关联的目标实体(如果无法从被引用的对象类型推断出它)
-
配置必须层叠到关联目标的操作:例如,如果删除了拥有实体,则确保还删除关联的目标
表 1-30 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-63 和示例 1-64 显示了如何使用此批注在 Customer
(拥有方)和 CustomerRecord
(被拥有方)之间配置一个一对一映射。
示例 1-63 @OneToOne - Customer 类
@Entity
public class Customer implements Serializable {
...
@OneToOne(optional=false)
@JoinColumn(name="CUSTREC_ID", unique=true, nullable=false, updatable=false)
public CustomerRecord getCustomerRecord() {
return customerRecord;
}
...
}
示例 1-64 @OneToOne - CustomerRecord 类
@Entity
public class CustomerRecord implements Serializable {
...
@OneToOne(optional=false, mappedBy="customerRecord")
public Customer getCustomer() {
return customer;
}
...
}
@OrderBy
默认情况下,JPA 持续性提供程序按关联实体的主键以升序顺序检索 Collection
关联的成员。
将 @OrderBy
批注与 @OneToMany 和 @ManyToMany 一起使用以便:
表 1-31 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-65 显示了如何使用 @OrderBy
批注指定 Project 方法 getEmployees
应按 Employee
字段 lastname
以升序顺序并按 Employee
字段 seniority
以降序顺序返回 Employee
的List
。示例 1-66 显示了默认情况下,Employee
方法 getProjects
按 Employee
主键 empId
以升序顺序返回 List
。
示例 1-65 Project 实体
@Entity public class Project {
...
@ManyToMany
@OrderBy("lastname ASC", "seniority DESC")
public List<Employee> getEmployees() {
...
};
...
}
示例 1-66 Employee 实体
@Entity public class Employee {
@Id
private int empId;
...
private String lastname;
...
private int seniority;
...
@ManyToMany(mappedBy="employees")
// By default, returns a List in ascending order by empId
public List<Project> getProjects() {
...
};
...
}
@PersistenceContext
在使用 JPA 持续性提供程序的应用程序中,可以使用实体管理器执行所有持续性操作(创建、读取、更新和删除)。Java EE 应用程序使用相关性注入或在 JNDI 名称空间中直接查找实体管理器获取实体管理器。
使用 @PersistenceContext
批注获取实体管理器:
如果有多个要指定的 @PersistenceContext
,则必须使用 @PersistenceContexts。
表 1-32 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-67 显示了如何使用此批注在无状态会话中注入实体管理器,示例 1-68 显示了如何使用此批注在 JNDI 中查找实体管理器。
示例 1-67 使用 @PersistenceContext 和相关性注入
@Stateless
public class OrderEntryBean implements OrderEntry {
@PersistenceContext
EntityManager em;
public void enterOrder(int custID, Order newOrder) {
Customer cust = em.find(Customer.class, custID);
cust.getOrders().add(newOrder);
newOrder.setCustomer(cust);
}
}
示例 1-68 使用 @PersistenceContext 和 JNDI 查找
@Stateless
public class OrderEntryBean implements OrderEntry {
@PersistenceContext(name="OrderEM")
EntityManager em;
public void enterOrder(int custID, Order newOrder) {
em = (EntityManager)ctx.lookup("OrderEM");
Customer cust = em.find(Customer.class, custID);
cust.getOrders().add(newOrder);
newOrder.setCustomer(cust);
}
}
@PersistenceContexts
如果要指定多个 @PersistenceContext,则必须使用一个 @PersistenceContexts
批注指定所有持续性上下文。
表 1-33 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-69 显示了如何使用此批注指定两个持续性上下文。
示例 1-69 @PersistenceContexts
@Stateless
public class OrderEntryBean implements OrderEntry {
@PersistenceContexts({
@PersistenceContext(name="OrderEM")
@PersistenceContext(name="ItemEM"),
})
public void enterOrder(int custID, Order newOrder) {
EntityManager em = (EntityManager)ctx.lookup("OrderEM");
Customer cust = em.find(Customer.class, custID);
cust.getOrders().add(newOrder);
newOrder.setCustomer(cust);
}
public void enterItem(int orderID, Item newItem) {
EntityManager em = (EntityManager)ctx.lookup("ItemEM");
...
}
}
@PersistenceProperty
默认情况下,JPA 持续性提供程序假设您使用 @PersistenceContext 获取的实体管理器将使用默认属性。
使用 @PersistenceProperty
批注指定属性(包括供应商特定的属性),以便容器或持续性提供程序:
创建实体管理器时将属性传递给持续性提供程序。无法识别的属性被简单地忽略。
表 1-34 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-70 显示了如何使用 @PersistenceProperty
批注自定义查询以利用由 TopLink Essentials 提供的供应商 JPA 扩展:在该示例中,该属性确保在此持续性上下文中使用一个完整的 TopLink 缓存。有关详细信息,请参阅“TopLink JPA Persistence.xml 文件扩展”。
示例 1-70 @PersistenceProperty
@Stateless
public class OrderEntryBean implements OrderEntry {
@PersistenceContext(
properties={
@PersistenceProperty={name="toplink.cache.type.default", value="CacheType.Full"}
}
)
EntityManager em;
public void enterOrder(int custID, Order newOrder) {
Customer cust = em.find(Customer.class, custID);
cust.getOrders().add(newOrder);
newOrder.setCustomer(cust);
}
}
@PersistenceUnit
默认情况下,JPA 持续性提供程序使用与默认持续性单元或您使用 @PersistenceContext 属性 unitName
指定的持续性单元关联的默认 EntityManagerFactory
。
使用 @PersistenceUnit
批注指定EntityManagerFactory
,您希望 JPA 持续性提供程序使用它来:
如果有多个要指定的 @PersistenceUnit
,则必须使用 @PersistenceUnits。
表 1-34 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-71 显示了如何使用 @PersistenceUnit
批注指定要使用的 EntityManagerFactory
的 JNDI 名称以及与该工厂关联的持续性单元名称。当 JPA 持续性提供程序使用 JNDI 获取一个使用持续性上下文 OrderEM
的实体管理器时,它将使用 JNDI 名称 OrderEMFactory
与持续性单元 OrderEMUnit
关联的 EntityManagerFactory
。
示例 1-71 使用 @PersistenceUnit 指定工厂和单元
@Stateless
public class OrderEntryBean implements OrderEntry {
@PersistenceContext(name="OrderEM")
@PersistenceUnit(name="OrderEMFactory", unitName="OrderEMUnit")
EntityManager em;
public void enterOrder(int custID, Order newOrder) {
em = (EntityManager)ctx.lookup("OrderEM");
Customer cust = em.find(Customer.class, custID);
cust.getOrders().add(newOrder);
newOrder.setCustomer(cust);
}
}
示例 1-72 显示了一个使用 @PersistenceContext 属性 unitName
仅指定持续性单元的其他方法。在该示例中,当 JPA 持续性提供程序使用 JNDI 获取一个使用持续性上下文OrderEM
的实体管理器时,它将使用与持续性单元 OrderEMUnit
关联的默认 EntityManagerFactory
。
示例 1-72 使用 @PersistenceContext 属性 unitName
@Stateless
public class OrderEntryBean implements OrderEntry {
@PersistenceContext(name="OrderEM", unitName="OrderEMUnit")
EntityManager em;
public void enterOrder(int custID, Order newOrder) {
em = (EntityManager)ctx.lookup("OrderEM");
Customer cust = em.find(Customer.class, custID);
cust.getOrders().add(newOrder);
newOrder.setCustomer(cust);
}
}
@PersistenceUnits
如果要指定多个 @PersistenceUnit,则必须使用一个 @PersistenceUnits
批注指定所有持续性上下文。
表 1-36 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-73 显示了如何使用此批注指定两个持续性单元。在该示例中,@PersistenceContext 属性 unitName
和 @PersistenceUnit
属性 unitName
必须对应以便关联持续性上下文和持续性单元。
示例 1-73 @PersistenceUnits
@Stateless
public class OrderEntryBean implements OrderEntry {
@PersistenceContexts({
@PersistenceContext(name="OrderEM", unitName="OrderEMUnit")
@PersistenceContext(name="ItemEM", unitName="ItemEMUnit"),
})
@PersistenceUnits({
@PersistenceUnit(name="OrderEMFactory", unitName="OrderEMUnit"),
@PersistenceUnit(name="ItemEMFactory", unitName="ItemEMUnit")
})
public void enterOrder(int custID, Order newOrder) {
EntityManager em = (EntityManager)ctx.lookup("OrderEM");
Customer cust = em.find(Customer.class, custID);
cust.getOrders().add(newOrder);
newOrder.setCustomer(cust);
}
public void enterItem(int orderID, Item newItem) {
EntityManager em = (EntityManager)ctx.lookup("ItemEM");
...
}
}
@PrimaryKeyJoinColumn
默认情况下,当一个实体使用 InheritanceType.JOINED
(请参阅 @Inheritance)扩展另一个实体时,JPA 持续性提供程序假设子类的外键列与超类主表的主键列同名。
使用 @PrimaryKeyJoinColumn
批注:
-
如果子类的外键列与该情形中超类的主表的主键列不同名
-
使用 @SecondaryTable 批注将辅助表连接到主表
-
在 @OneToOne 映射中,引用实体的主键用作被引用实体的外键。
-
使用复合外键(请参阅 @PrimaryKeyJoinColumns)
表 1-37 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-74 显示了一个实体基类 Customer
,示例 1-75 显示了如何使用 @PrimaryKeyJoinColumn
在 ValuedCustomer
(Customer
的一个子类)的主表中指定主键连接列 CUST_ID
。
示例 1-74 @PrimaryKeyJoinColumn - InheritanceType.JOINED 超类
@Entity
@Table(name="CUST")
@Inheritance(strategy=JOINED)
@DiscriminatorValue("CUST")
public class Customer {
...
}
示例 1-75 @PrimaryKeyJoinColumn - InheritanceType.JOINED 子类
@Entity
@Table(name="VCUST")
@DiscriminatorValue("VCUST")
@PrimaryKeyJoinColumn(name="CUST_ID")
public class ValuedCustomer extends Customer {
...
}
@PrimaryKeyJoinColumns
默认情况下,JPA 持续性提供程序假设每个实体有一个单列主键。
如果要指定一个由两个或更多列组成的主键,请使用 @PrimaryKeyJoinColumns
批注。
表 1-38 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-76 显示了如何使用此批注指定一个由列 CUST_ID
和 CUST_TYPE
组成的复合主键。
示例 1-76 @PrimaryKeyJoinColumns
@Entity
@Table(name="VCUST")
@DiscriminatorValue("VCUST")
@PrimaryKeyJoinColumns({
@PrimaryKeyJoinColumn(name="CUST_ID",referencedColumnName="ID"),
@PrimaryKeyJoinColumn(name="CUST_TYPE",referencedColumnName="TYPE")
})
public class ValuedCustomer extends Customer {
...
}
@QueryHint
默认情况下,JPA 持续性提供程序假设 @NamedQuery 或 @NamedNativeQuery 应完全按照查询 String
指定的方式执行。
使用 @QueryHint
批注指定供应商特定的 JPA 查询扩展,以:
表 1-6 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-77 显示了如何使用 @QueryHint
批注自定义查询以利用由 TopLink Essentials 提供的供应商 JPA 扩展:在该示例中,提示确保在执行查询时始终刷新 TopLink 缓存。有关详细信息,请参阅“TopLink JPA 查询提示扩展”。
示例 1-77 @QueryHint
@Entity
@NamedQuery(
name="findAllEmployees",
query="SELECT * FROM EMPLOYEE WHERE MGR=1"
hints={@QueryHint={name="toplink.refresh", value="true"}}
)
public class Employee implements Serializable {
...
}
@SecondaryTable
默认情况下,JPA 持续性提供程序假设实体的所有持久字段均存储到一个名称为实体名称的数据库表中:该表称作主表(请参阅 @Table)。
如果希望 JPA 分别将实体的某些持久字段持久保存到主表和其他数据库表,请使用 @SecondaryTable
批注将实体与其他数据库表关联。在该示例中,您使用 @Column 批注将实体的持久字段与表关联。
如果要将两个或更多辅助表与实体关联,则可以使用 @SecondaryTables。
表 1-40 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-78 显示了如何使用此批注指定一个名为 EMP_HR
的辅助表。在该示例中,默认情况下,JPA 将实体持久字段 empId
持久保存到名为 Employee
的主表中的列 empId
,并将empSalary
持久保存到辅助表 EMP_HR
中的列 empSalary
。有关详细信息,请参阅 @Column。
示例 1-78 @SecondaryTable
@Entity
@SecondaryTable(name="EMP_HR")
public class Employee implements Serializable {
...
private Long empId;
@Column(table="EMP_HR", name="EMP_SALARY"))
private Float empSalary;
...
}
@SecondaryTables
如果需要指定多个 @SecondaryTable,可以使用一个 @SecondaryTables
批注指定所有辅助表。
表 1-41 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-79 显示了如何使用此批注指定两个名为 EMP_HR
和 EMP_TR
的辅助表。在该示例中,默认情况下,JPA 将实体持久字段 empId
持久保存到名为 Employee
的主表中的列empId
。JPA 将 empSalary
持久保存到辅助表 EMP_HR
中的列 empSalary
,并将 empClass
持久保存到辅助表 EMP_TR
中的列 EMP_HR
。有关详细信息,请参阅 @Column。
示例 1-79 @SecondaryTables
@Entity
@SecondaryTables({
@SecondaryTable(name="EMP_HR"),
@SecondaryTable(name="EMP_TR")
})
public class Employee implements Serializable {
...
private Long empId;
@Column(table="EMP_HR", name="EMP_SALARY"))
private Float empSalary;
@Column(table="EMP_TR", name="EMP_CLASS"))
private Float empClass;
...
}
@SequenceGenerator
如果使用 @GeneratedValue 批注指定一个 SEQUENCE
类型的主键生成器,则可以使用 @SequenceGenerator
批注微调该主键生成器以:
表 1-42 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-80 显示了如何使用此批注为名为 CUST_SEQ
的 SEQUENCE
主键生成器指定分配大小。
示例 1-80 @SequenceGenerator
@Entity
public class Employee implements Serializable {
...
@Id
@SequenceGenerator(name="CUST_SEQ", allocationSize=25)
@GeneratedValue(strategy=SEQUENCE, generator="CUST_SEQ")
@Column(name="CUST_ID")
public Long getId() {
return id;
}
...
@SqlResultSetMapping
执行 @NamedNativeQuery 时,它可以返回实体(包括不同类型的实体)、标量值或实体和标量值的组合。
默认情况下(如示例 1-81 所示),JPA 持续性提供程序假设原生 SQL 查询中的 SELECT
语句:
示例 1-81 简单的原生 SQL 查询
Query q = entityManager.createNativeQuery(
"SELECT o.id, o.quantity, o.item " +
"FROM Order o, Item i " +
"WHERE (o.item = i.id) AND (i.name = "widget")",
Order.class
);
List resultList = q.getResultList();
// List of Order entity objects:{Order, Order, ...}
如果原生 SQL 查询满足以下条件,请使用 @SqlResultSetMapping
批注控制 JPA 持续性提供程序如何将 JDBC 结果集映射到实体字段或属性以及标量:
-
返回多个类型的实体
-
只返回标量值或实体和标量值的组合
-
使用列别名(AS
语句)
如果有多个 @SqlResultSetMapping
,则必须使用 @SqlResultSetMappings。
表 1-8 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-82 显示了如何使用此批注将 Order
和 Item
(请参阅示例 1-83)实体和标量 name
包含在结果列表(请参阅示例 1-84)中。在该示例中,结果列表将为 Object
数组的List
,如:{[Order, Item, "Shoes"], [Order, Item, "Socks"], ...}
。
示例 1-82 使用 @SqlResultSetMapping 的 Order 实体
@SqlResultSetMapping(
name="OrderResults",
entities={
@EntityResult(
entityClass=Order.class,
fields={
@FieldResult(name="id", column="order_id"),
@FieldResult(name="quantity", column="order_quantity"),
@FieldResult(name="item", column="order_item")
}
),
@EntityResult(
entityClass=Item.class,
fields={
@FieldResult(name="id", column="item_id"),
@FieldResult(name="name", column="item_name"),
}
)
}
columns={
@ColumnResult(
name="item_name"
)
}
)
@Entity
public class Order {
@Id
protected int id;
protected long quantity;
protected Item item;
...
}
示例 1-83 Item 实体
@Entity
public class Item {
@Id
protected int id;
protected String name;
...
}
示例 1-84 将 @SqlResultSetMapping 与 @EntityResult 一起使用的原生查询
Query q = entityManager.createNativeQuery(
"SELECT o.id AS order_id, " +
"o.quantity AS order_quantity, " +
"o.item AS order_item, " +
"i.id AS item_id, " +
"i.name AS item_name, " +
"FROM Order o, Item i " +
"WHERE (order_quantity > 25) AND (order_item = i.id)",
"OrderResults"
);
List resultList = q.getResultList();
// List of Object arrays:{[Order, Item, "Shoes"], [Order, Item, "Socks"], ...}
@SqlResultSetMappings
如果需要指定多个 @SqlResultSetMapping,则必须使用一个 @SqlResultSetMappings
批注指定所有 SQL 结果集映射。
表 1-5 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-85 显示了如何使用此批注指定两个 @SqlResultSetMapping 实例。
示例 1-85 @SqlResultSetMappings
SqlResultSetMappings({
@SqlResultSetMapping(
name="OrderItemItemNameResults",
entities={
@EntityResult(entityClass=Order.class),
@EntityResult(entityClass=Item.class)
}
columns={
@ColumnResult(name="item_name")
}
),
@SqlResultSetMapping(
name="OrderItemResults",
entities={
@EntityResult(entityClass=Order.class),
@EntityResult(entityClass=Item.class)
}
)
})
@Entity
public class Order {
@Id
protected int id;
protected long quantity;
protected Item item;
...
}
@Table
默认情况下,JPA 持续性提供程序假设实体的所有持久字段均存储到一个名称为实体名称的数据库表中(请参阅 @Entity)。
在以下条件下,使用 @Table
批注指定与实体关联的主表:
如果希望 JPA 将某些字段持久保存到主表,而将其他字段持久保存到一个或多个辅助表,请参阅 @SecondaryTable。
表 1-45 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-86 显示了如何使用此批注指定主表名。
示例 1-86 @Table
@Entity
@Table(name="EMP")
public class Employee implements Serializable {
...
}
@TableGenerator
如果使用 @GeneratedValue 批注指定一个 TABLE
类型的主键生成器,可以使用 @TableGenerator
批注微调该主键生成器以:
表 1-46 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-87 显示了如何使用此批注为名为 empGen
的 TABLE
主键生成器指定分配大小。
示例 1-87 @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 一起使用。
表 1-14 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-88 显示了如何使用此批注指定 JPA 持续性提供程序应将 java.util.Date
字段 startDate
持久保存为 DATE
(java.sql.Date
) 数据库类型。
示例 1-88 @Temporal
@Entity
public class Employee {
...
@Temporal(DATE) protected java.util.Date startDate;
...
}
@Transient
默认情况下,JPA 持续性提供程序假设实体的所有字段均为持久字段。
使用 @Transient
批注指定实体的非持久字段或属性,例如,一个在运行时使用但并非实体状态一部分的字段或属性。
JPA 持续性提供程序不会对批注为 @Transient
的属性或字段持久保存(或创建数据库模式)。
该批注可以与 @Entity、@MappedSuperclass 和 @Embeddable 一起使用。
该批注没有属性。有关更多详细信息,请参阅 API。
示例 1-89 显示了如何使用此批注将 Employee
字段 currentSession
指定为非持久字段。JPA 持续性提供程序将不持久保存该字段。
示例 1-89 @Transient
@Entitypublic class Employee { @Id int id; @Transient Session currentSession; ...}
@UniqueConstraint
默认情况下,JPA 持续性提供程序假设所有列均可以包含重复值。
使用 @UniqueConstraint
批注指定将在为主表或辅助表生成的 DDL 中包含一个唯一约束。或者,您可以在列级别指定唯一约束(请参阅 @Column)。
表 1-48 列出了此批注的属性
。有关更多详细信息,请参阅 API。
示例 1-90 显示了如何使用此批注对主表 EMP
中的列 EMP_ID
和 EMP_NAME
指定一个唯一约束。
示例 1-90 使用唯一约束的 @Table
@Entity
@Table(
name="EMP",
uniqueConstraints={@UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})}
)
public class Employee implements Serializable {
...
}
@Version
默认情况下,JPA 持续性提供程序假设应用程序负责数据一致性。
使用 @Version
批注通过指定用作其乐观锁定值的实体类的版本字段或属性来启用 JPA 管理的乐观锁定(推荐做法)。
选择版本字段或属性时,确保:
此批注没有属性
。有关更多详细信息,请参阅 API。
示例 1-91 显示了如何使用此批注将属性 getVersionNum 指定为乐观锁定值。在该示例中,该属性的列名设置为 OPTLOCK(请参阅 @Column),而非属性的默认列名。
示例 1-91 @Version
@Entity
public class Employee implements Serializable {
...
@Version
@Column(name="OPTLOCK")
protected int getVersionNum() {
return versionNum;
}
...
}
生命周期事件批注
如果需要在生命周期事件期间执行自定义逻辑,请使用以下生命周期事件批注关联生命周期事件与回调方法:
-
@PostLoad
-
@PostPersist
-
@PostRemove
-
@PostUpdate
-
@PrePersist
-
@PreRemove
-
@PreUpdate
图 1-1 演示了 JPA 支持的实体生命周期事件之间的关系。
可以直接对实体方法进行批注,也可以指定一个或多个实体监听程序类(请参阅 @EntityListeners)。
如果直接对实体方法进行批注,则该实体方法必须满足以下要求: