本文的主要内容如下:
详细解释了下面10个批注的含义及其批注所包含的属性:
@Entity
@EntityListeners
@EntityResult
@Enumerated
@ExcludeDefaultListeners
@ExcludeSuperclassListeners
@FieldResult
@GeneratedValue
@Id
@IdClass
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
@Entity
使用 @Entity
批注将普通的旧式 Java 对象 (POJO) 类指定为实体,并使其可用于 JPA 服务。必须将 POJO 类指定为实体,然后才可以使用任何其他 JPA 批注。
表 1-11 列出了此批注的属性。有关更多详细信息,请参阅 API。
表 1-11 @Entity 属性
属性 | 必需 | 说明 |
---|---|---|
name |
| 默认值:JPA 持续性提供程序假设实体名称是实体类的名称。在示例 1-20 中,默认 name 为“Employee”。 如果实体类名难于处理、是一个保留字、与事先存在的数据模型不兼容或作为数据库中的表名无效,请将 name 设置为其他 String 值。 |
示例 1-20 显示了该批注的用法。
示例 1-20 @Entity
@Entity
public class Employee implements Serializable {
...
}
@EntityListeners
可以使用生命周期批注(请参阅生命周期事件批注)指定实体中的方法,这些方法在指定的生命周期事件发生时执行您的逻辑。
使用 @EntityListeners
批注将一个或多个实体监听程序类与 @Entity 或 @MappedSuperclass 关联,条件是您需要在指定的生命周期事件发生时执行逻辑,以及:
-
不希望在实体 API 中公开生命周期监听程序方法。
-
要在不同的实体类型之间共享生命周期监听程序逻辑。
当实体或子类上发生生命周期事件时,JPA 持续性提供程序将按监听程序定义的顺序通知每个实体监听程序,并调用使用相应的生命周期事件类型进行批注的实体监听程序方法(如果有)。
实体监听程序类具有以下特征:
-
它是一个普通的旧式 Java 对象 (POJO) 类
-
它有一个或多个具有以下签名的回调方法:
public void <MethodName>(Object)
可以指定参数类型
Object
,或实体监听程序将与其关联的实体类的类型。 -
它用一个或多个生命周期事件批注对每个回调方法进行批注。
一个生命周期事件只能与一个回调监听程序方法关联,但某个给定的回调监听程序方法可以与多个生命周期事件关联。
如果使用实体监听程序,则可以管理那些实体监听程序使用 @ExcludeDefaultListeners 和 @ExcludeSuperclassListeners 调用。
表 1-12 列出了此批注的属性。有关更多详细信息,请参阅 API。
表 1-12 @EntityListeners 属性
属性 | 必需 | 说明 |
---|---|---|
value |
| 要为 @Entity 或 @MappedSuperclass 指定实体监听程序类的列表,请将 value 设置为实体监听程序类的 Class 数组。 |
示例 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-13
列出了此批注的属性。有关更多详细信息,请参阅 API
。
表 1-13 @EntityResult 属性
属性 | 必需 | 说明 |
---|---|---|
entityClass |
| 将 entityClass 设置为由 SELECT 语句返回的实体的 Class。 |
discriminatorColumn |
| 默认值:空 String。默认情况下,JPA 持续性提供程序假设 SELECT 语句中不包含标识符列(请参阅 @Inheritance)。如果在 SELECT 语句中使用标识符列,请将 discriminatorColumn 设置为所使用的 String 列名。 |
fields |
| 默认值:空 FieldResult 数组。默认情况下,JPA 持续性提供程序假设 SELECT 语句包含与返回的实体的所有字段或属性相对应的所有列,且 SELECT 语句中的列名对应于字段或属性名(未使用 AS 语句)。如果 SELECT 语句只包含某些与返回的实体的字段或属性相对应的列,或 SELECT 语句中的列名并不对应于字段或属性名(使用了 AS 语句),请将 fields 设置为 @FieldResult 的数组,SELECT 语句中的每一列一个 @FieldResult。 |
示例 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-14 @Enumerated 属性
属性 | 必需 | 说明 |
---|---|---|
value |
| 默认值:EnumType.ORDINAL。默认情况下,JPA 持续性提供程序假设对于映射到枚举常量的属性或字段,应持久保存序数值。在示例 1-28 中,当持久保存 Employee 时,EmployeeStatus 的序数值将写入数据库。如果需要持久保存的枚举常量的 String 值,请将 value 设置为 EnumType.STRING。 |
根据示例 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-15 @FieldResult 属性
属性 | 必需 | 说明 |
---|---|---|
column |
| 将 column 设置为 SELECT 语句中使用的列的 String 名称。如果在 SELECT 中使用列别名(AS 语句),请将 column 设置为列别名。 |
name |
| 将 name 设置为实体的字段或属性名(作为 String),该名称对应于 column 属性指定的列名。 |
示例 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-16 @GeneratedValue 属性
属性 | 必需 | 说明 |
---|---|---|
generator |
| 默认值:JPA 持续性提供程序为它选择的主键生成器分配一个名称。如果该名称难于处理、是一个保留字、与事先存在的数据模型不兼容或作为数据库中的主键生成器名称无效,则将 generator 设置为要使用的 String 生成器名称。 |
strategy |
| 默认值:GenerationType.AUTO。默认情况下,JPA 持续性提供程序选择最适合于基础数据库的主键生成器类型。如果您感觉另一个生成器类型更适合于数据库或应用程序,请将 strategy 设置为所需的 GeneratorType:● IDENTITY — 指定持续性提供程序使用数据库身份列● AUTO — 指定持续性提供程序应选择一个最适合于基础数据库的主键生成器。● SEQUENCE — 指定持续性提供程序使用数据库序列(请参阅@SequenceGenerator)● TABLE — 指定持续性提供程序为使用基础数据库表的实体分配主键以确保唯一性(请参阅 @TableGenerator) |
示例 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 批注将一个或多个持久字段或属性指定为实体的主键。
对于每个实体,必须至少指定以下项之一:
● 一个 @Id
● 多个 @Id 和一个 @IdClass(对于复合主键)
● 一个 @EmbeddedId
此批注没有属性。有关更多详细信息,请参阅 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-17 @IdClass 属性
属性 | 必需 | 说明 |
---|---|---|
value |
| 要指定复合主键类,请将 value 设置为所需的 Class(请参阅 @AttributeOverride)。 |
示例 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;
...
}