Hibernate / JPA2 持久化注解教程

Hibernate / JPA2 持久化注解教程

原文: https://howtodoinjava.com/hibernate/hibernate-jpa-2-persistence-annotations-tutorial/

Hibernate(或 JPA2)持久化注解教程包含对 Java POJO 进行注解以使其充当持久化 JPA 实体时可能需要的所有重要注解的概述。 本教程首先定义一个 POJO “EmployeeEntity”,在其中定义一些属性,并具有各自的获取器和设置器方法。 在学习新注解时,我们将这些注解应用于EmployeeEntity,然后我们将了解该特定注解的含义。

让我们快速列出注解,我们将在本教程中进行讨论。

Table of Contents

**Most used JPA Annotations**
	Entity Beans with `@Entity`
	Primary Keys with `@Id` and `@GeneratedValue`
	Generating Primary Key Values with `@SequenceGenerator`
	Generating Primary Key Values with `@TableGenerator`
	Compound Primary Keys with `@Id`, `@IdClass`, or `@EmbeddedId`
	Database Table Mapping with `@Table` and `@SecondaryTable`
	Persisting Basic Types with `@Basic`
	Omitting Persistence with `@Transient`
	Mapping Properties and Fields with `@Column`
**Modeling Entity Relationships**
**Mapping Inheritance Hierarchies**
	Single Table
	Joined Table
	Table per Concrete Class
**Other JPA2 Persistence Annotations**
	Temporal Data with `@Temporal`
	Element Collections with `@ElementCollection`
	Large Objects with `@Lob`
	Mapped Superclasses with `@MappedSuperclass`
	Ordering Collections with `@OrderColumn`
**Named Queries (HQL or JPQL)**
	`@NamedQuery` and `@NamedQueries`
	Named Native Queries using `@NamedNativeQuery`

上面的列表是一个很大的列表,列出的项目的完整详细信息的范围较小,但是我将尝试包括所有相关信息,这将有助于您做出决定。 首先,我们写下EmployeeEntity POJO,然后在整个讨论中开始使用JPA annotations装饰它。

package com.howtodoinjava.demo.test;

import java.io.Serializable;

public class EmployeeEntity implements Serializable
{
   private static final long serialVersionUID = 570235488278506106L;

   private Integer           employeeId;
   private String            firstName;
   private String            lastName;

   public Integer getEmployeeId()
   {
      return employeeId;
   }

   public void setEmployeeId(Integer employeeId)
   {
      this.employeeId = employeeId;
   }

   public String getFirstName()
   {
      return firstName;
   }

   public void setFirstName(String firstName)
   {
      this.firstName = firstName;
   }

   public String getLastName()
   {
      return lastName;
   }

   public void setLastName(String lastName)
   {
      this.lastName = lastName;
   }
}

最常用的 JPA 注解

Hibernate / JPA2 @Entity注解教程

这是将 POJO 标记为 JPA 实体的第一步。 为此,我们需要应用@Entity注解,如下所示:

import javax.persistence.Entity;

@Entity
public class EmployeeEntity implements Serializable
{
	public EmployeeEntity(){

	}
	//Other code
}

JPA2 标准注解包含在javax.persistence包中,因此我们从此包中导入了注解。 @Entity注解将该类标记为实体 Bean,因此它必须具有无参数构造器,该构造器至少在受保护的范围(特定于 JPA)下可见。 Hibernate 至少支持包作用域,但是您会失去对其他 JPA 实现的可移植性,因为它们可能只允许受保护级别作用域。 理想情况下,您应该将此构造器公开,这也使其与其他规范高度兼容。 还有更多规则,例如 POJO 类的欲望不是最终的。 而且也不能是抽象的。

现在,让我们快速浏览一下“@org.hibernate.annotations.Entity”注解(特定于 Hiberate)。 使用它,您可以为特定于 Hiberate 的实体定义一些额外的行为。

@javax.persistence.Entity仍然是强制性的,@org.hibernate.annotations.Entity是一种鼓励,而不是替代。

@javax.persistence.Entity
@org.hibernate.annotations.Entity(
      selectBeforeUpdate = true,
      dynamicInsert = true, dynamicUpdate = true,
      optimisticLock = OptimisticLockType.ALL,
      polymorphism = PolymorphismType.EXPLICIT,
      mutable = false)
public class EmployeeEntity implements Serializable
{
	public EmployeeEntity(){

	}
	//Other code
}

所有上述属性在很久以前就已被标记为已弃用,但仍受支持。 我仅给出示例供参考。

实际上,在新版本的 Hiberate 下,您根本不需要使用@org.hibernate.annotations.Entity。 相反,您可以仅使用注解直接添加所需的行为。 让我们看一下上面编写的实体的等效代码。

import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.annotations.OptimisticLocking;
import org.hibernate.annotations.Polymorphism;
import org.hibernate.annotations.PolymorphismType;
import org.hibernate.annotations.SelectBeforeUpdate;

@javax.persistence.Entity
@SelectBeforeUpdate
@DynamicInsert
@DynamicUpdate
@Polymorphism (type = PolymorphismType.EXPLICIT)
@OptimisticLocking (type = OptimisticLockType.ALL)
public class EmployeeEntity implements Serializable
{
	public EmployeeEntity(){

	}
	//Other code
}

继续,如果需要,在代码中使用这些注解。

使用@Id@GeneratedValue的主键

每个实体 bean 必须具有一个主键,您可以在主类上使用@Id注解对其进行注解。 通常,主键将是单个字段,尽管它也可以是多个字段的组合,我们将在后面的部分中看到。

@Id注解的位置决定了 Hibernate 将用于映射的默认访问策略。 如果将注解应用于字段,如下所示,则将使用“字段访问”。

@Id
private Integer employeeId;

相反,如果将注解应用于字段的访问器,则将使用属性访问。

@Id
public Integer getEmployeeId()
{
  return employeeId;
}

属性访问意味着 Hibernate 将调用获取器和设置器,而不是直接直接设置字段,而在字段访问的情况下,它会这样做。 如果需要,这可以灵活地更改在id字段中设置的实际值的值。 此外,您还可以在设置器中为其他字段设置“id”字段时应用额外的逻辑。

默认情况下,@Id注解不会创建主键生成策略,这意味着作为代码的作者,您需要通过设置显式调用设置器方法的方式来确定有效的主键。 或者,您可以使用@GeneratedValue注解。

@GeneratedValue注解具有一对属性:strategygenerator 如下:

@Id
@GeneratedValue (strategy = GenerationType.SEQUENCE)
private Integer employeeId;

//OR a more complex use can be

@Id
@GeneratedValue(strategy=GenerationType.TABLE , generator="employee_generator")
@TableGenerator(name="employee_generator",
			   table="pk_table",
			   pkColumnName="name",
			   valueColumnName="value",
			   allocationSize=100)
private Integer employeeId;

策略属性必须是javax.persistence.GeneratorType枚举中的值。 如果未指定生成器类型,则默认为AUTOGeneratorType上有四种不同类型的主键生成器,如下所示:

  1. AUTO:Hibernate 根据数据库对主键生成的支持来决定使用哪种生成器类型。
  2. IDENTITY:数据库负责确定和分配下一个主键。
  3. SEQUENCE:某些数据库支持SEQUENCE列类型。 它使用@SequenceGenerator
  4. TABLE:此类型保留一个带有主键值的单独表。 它使用@TableGenerator

generator属性允许使用上面的代码示例中所示的自定义生成机制。

使用@SequenceGenerator生成主键值

序列是一个数据库对象,可用作主键值的源。 它与标识列类型的使用类似,不同之处在于序列独立于任何特定表,因此可以被多个表使用。

要声明要使用的特定序列对象及其属性,必须在带注解的字段上包含@SequenceGenerator注解。 这是一个例子:

@Id
@SequenceGenerator(name="seq1",sequenceName="HIB_SEQ")
@GeneratedValue(strategy=SEQUENCE,generator="seq1")
private Integer employeeId;

在此,已声明了名为seq1的序列生成注解。 这指的是称为HIB_SEQ的数据库序列对象。 然后,将名称seq1引用为@GeneratedValue注解的生成器属性。 仅序列生成器名称是必需的; 其他属性将采用合理的默认值,但是作为一种好的做法,您应该为sequenceName属性提供一个明确的值。 如果未指定,则持久化供应器将选择要使用的sequenceName值。

使用@TableGenerator生成主键值

@TableGenerator注解的使用方式与@SequenceGenerator注解非常相似,但是由于@TableGenerator操纵标准数据库表来获取其主键值,因此无需使用特定于供应商的序列对象,因此可以保证 在数据库平台之间可移植。

为了获得最佳的可移植性和最佳性能,您不应指定使用表生成器,而应使用@GeneratorValue(strategy=GeneratorType.AUTO)配置,该配置允许持久性提供程序为数据库选择最合适的策略。

与序列生成器一样,@TableGenerator的名称属性是必需的,其他属性是可选的,并且表详细信息由持久化供应器选择。 让我们再来看一个例子。

@Id
@GeneratedValue(strategy=GenerationType.TABLE , generator="employee_generator")
@TableGenerator(name="employee_generator",
			   table="pk_table",
			   pkColumnName="name",
			   valueColumnName="value",
			   allocationSize=100)
private Integer employeeId;

可选属性如下:

  • allocationSize:允许一次设置主键的数量以提高性能。
  • catalog:允许指定表所在的目录。
  • initialValue:允许指定起始主键值。
  • pkColumnName:允许标识表的主键列。 该表可以包含为多个实体生成主键值所需的详细信息。
  • pkColumnValue:允许标识包含主键生成信息的行的主键。
  • schema:允许指定表所在的模式。
  • table:包含主键值的表的名称。
  • uniqueConstraints:允许将其他约束应用于表以生成模式。
  • valueColumnName:允许标识包含当前实体的主键生成信息的列。

因为该表可用于包含各种条目的主键值,所以使用该表的每个实体可能只有一行。 因此,它需要自己的主键(pkColumnName),以及包含从中获取主键的任何实体要使用的下一个主键值(pkColumnValue)的列。

使用@Id@IdClass@EmbeddedId的复合主键

尽管出于各种原因使用单列代理键是有利的,但有时您可能不得不使用业务键。 当它们包含在单个列中时,您可以使用@Id而无需指定生成策略,该策略强制用户在持久保存实体之前分配主键值。

但是,如果是多列主键,则必须创建一个代表该主键的类。 当然,它不需要自己的主键,但它必须是公共类,必须具有默认构造器,必须可序列化,并且必须实现hashCode()equals()方法,以允许 Hibernate 代码测试主键冲突。

一旦创建了此主键类,就可以使用以下三种策略:

  1. 将其标记为@Embeddable并为其添加一个普通属性,并标记为@Id
  2. 向您的实体类添加一个普通属性,标记为@EmbeddableId
  3. 将属性的所有字段添加到您的实体类中,用@Id标记它们,并用@IdClass标记您的实体类,以提供主键类的类。

使用带有标记为@Embeddable的类的@Id是最自然的方法。 无论如何,@Embeddable标签可用于非主键可嵌入值。 它允许您将复合主键视为单个属性,并允许在其他表中重用@Embeddable类。

值得一提的是:嵌入式主键类必须可序列化。

可以在这里阅读详细的示例: http://docs.oracle.com/javaee/6/api/javax/persistence/Embeddable.html

使用@Table@SecondaryTable进行数据库表映射

默认情况下,表名称是从实体名称派生的。 因此,给定一个带有简单@Entity注解的Employee类,表名将为“employee”,并根据数据库的配置进行了调整。 如果实体名称发生更改(通过在@Entity注解中提供其他名称,例如@Entity("EMP_MASTER"),则新名称将用作表名。

可以进一步自定义表名,并且可以通过@Table注解配置其他与数据库相关的属性。 该注解允许您指定表的许多详细信息,这些详细信息将用于将实体保留在数据库中。 如前所述,如果省略注解,则 Hibernate 将默认使用类名作为表名,因此,如果要覆盖该行为,则只需提供此注解。 @Table注解提供了四个属性,使您可以覆盖表的名称,表的目录及其架构,并对表中的列实现唯一约束。 通常,您只能提供一个替代表名称,例如:@Table(name="ORDER_HISTORY")。 如果数据库模式是从带注解的类生成的,则将应用唯一约束,并将补充任何特定于列的约束。 否则不会强制执行。

@SecondaryTable注解提供了一种对实体 bean 进行建模的方法,该实体 bean 跨多个不同的数据库表保留。 在这里,除了为主数据库表提供@Table注解之外,您的实体 bean 还可以具有@SecondaryTable注解或包含零个或多个@SecondaryTable注解的@SecondaryTables注解。 @SecondaryTable注解具有与@Table注解相同的基本属性,但附加了join属性。 join属性定义主数据库表的连接列。 它接受javax.persistence.PrimaryKeyJoinColumn对象的数组。 如果省略join属性,则将假定表在相同名称的主键列上进行了连接。

从辅助表中提取实体中的属性时,必须使用@Column注解对其进行标记,并使用表属性标识适当的表。

@Entity
@Table(name = "employee")
@SecondaryTable(name = "employee_details")
public class EmployeeEntity implements Serializable
{
   @Id
   @GeneratedValue (strategy = GenerationType.SEQUENCE)
   private Integer employeeId;
   private String  firstName;
   private String  lastName;

   @Column(table = "employee_details")
   public String address;
}

通过在@Table@SecondaryTableuniqueConstraints属性中添加一个或多个适当的@UniqueConstraint注解,可以将主表或辅助表中的列标记为具有唯一值。 或者,您也可以在@Column属性上的unique属性在字段级别设置唯一性。

@Entity
@Table(
      name="employee",
      uniqueConstraints={@UniqueConstraint(columnNames="firstName")}
      )
@SecondaryTable(name = "employee_details")
public class EmployeeEntity implements Serializable{

}

使用@Basic保留基本类型

默认情况下,POJO 中的属性和实例变量是持久化的。 Hibernate 将为您存储它们的值。 因此,最简单的映射适用于“基本”类型。 这些包括基本类型,基本类型包装器,基本类型或包装器数组,枚举以及实现Serializable但本身不是映射实体的任何类型。

这些都隐式映射 - 无需注解。 默认情况下,此类字段被映射到单个列,并且热切获取用于检索它们(即,当从数据库中检索实体时,将检索所有基本字段和属性)。 同样,当字段或属性不是基元时,可以将其存储和检索为空值。

通过将@Basic注解应用于适当的类成员,可以覆盖此默认行为。 注解具有两个可选属性,并且本身是完全可选的。 第一个属性被命名为可选,并带有一个布尔值。 默认为true,可以将其设置为false,以提示架构生成应创建关联列NOT NULL。 第二个名为fetch,它采用枚举FetchType的成员。 默认情况下为EAGER,但可以设置为LAZY以允许在访问值时加载。

@Basic (fetch = FetchType.LAZY, optional = false)
private String  firstName;

延迟加载的使用不太可能有价值,除非将大型可序列化对象映射为基本类型(而不是为其提供给定的实体映射),并且检索时间可能变得很长。 虽然必须遵守(默认)EAGER值,但是LAZY标志被视为提示,并且可以由持久化引擎忽略。

通常会省略@Basic属性,而使用@Column属性,否则可能会使用@Basic注解的可选属性来提供NOT NULL行为。

使用@Transient省略持久化

某些字段(例如,计算值)只能在运行时使用,并且应将它们保留在数据库中,并从对象中将其丢弃。 JPA 规范为这些瞬态字段提供了@Transient注解。 @Transient注解没有任何属性 - 您只需将其添加到实例变量或适合实体 bean 的属性访问策略的设置器方法中即可。

@Transient注解突出显示了在 Hibernate 中使用注解和使用 XML 映射文档之间的重要区别之一。 使用注解,Hibernate 将默认保留所有字段在映射对象上的持久化。 使用 XML 映射文档时,Hibernate 要求您明确告诉它哪些字段将被保留。

例如,如果我们的EmployeeEntity有两个附加字段“age”和“dateOfBirth”,则您想将dateOfBirth存储在数据库中,但是您想根据dateOfBirth的值来计算运行时的年龄。 因此,“age”字段必须标记为瞬态。

@Transient
private Integer age;

使用@Column映射属性和字段

@Column注解用于指定字段或属性将映射到的列的详细信息。 其中一些细节与架构相关,因此仅在从注解文件生成架构时才适用。 其他应用则由 Hibernate(或 JPA2 持久化引擎)在运行时应用并强制执行。 它是可选的,具有一组适当的默认行为,但是在覆盖默认行为或需要将对象模型适合到预先存在的架构中时通常很有用。

以下属性通常被覆盖:

  1. name:允许明确指定列的名称 - 默认情况下,这将是属性的名称。
  2. length :允许显式定义用于映射值(尤其是String值)的列的大小。 列大小默认为 255,例如,否则可能会导致字符串数据被截断。
  3. nullable:允许在生成架构时将该列标记为NOT NULL。 默认设置是字段应允许为空; 但是,当字段是或应该是必填字段时,通常会覆盖此字段。
  4. unique:允许将该列标记为仅包含唯一值。 默认为false,但通常将其设置为一个值,该值可能不是主键,但是如果重复(例如用户名)仍会引起问题。
@Column(name="FNAME",length=100,nullable=false)
private String  firstName;

还有更多属性,这些属性在现实生活项目中很少使用。 这些是table, insertable, updatable, columnDefinition, precisionscale。 我将让您详细探讨它们。

建模实体关系

我已经在单独的详细帖子中介绍了与建模相关的概念。 请在这些链接的文章中阅读有关它们的更多信息,因为此处没有重复的信息是没有意义的。

  1. 建模@OneToOne关系
  2. 建模@OneToMany关系
  3. 建模@ManyToMany关系

映射继承层次结构

实体并不总是与其他实体关联为属性; 有时,它们使用常规的 OOP 继承规则进行关联。 Hibernate 允许您使用@Inheritance注解来履行此类关系。

JPA2 标准和 Hibernate 都支持将继承层次结构映射到数据库中的三种方法。 这些如下:

  1. 单个表(SINGLE_TABLE:每个类层次结构都有一个表
  2. 已连接(JOINED:每个子类一个表(包括接口和抽象类)
  3. 每类表(TABLE_PER_CLASS:每个具体类实现一个表

与继承相关的持久化实体必须使用@Inheritance注解进行标记。 这采用单个策略属性,该属性设置为与这些方法相对应的三个javax.persistence.InheritanceType枚举值之一(即SINGLE_TABLEJOINEDTABLE_PER_CLASS)。

让我们详细讨论一下。

单表

单表方法为主要超类及其所有子类型管理一个数据库表。 超类的每个映射字段或属性以及派生类型的每个不同字段或属性都有列。 遵循此策略时,您将需要确保在层次结构中任何字段或属性名称冲突时,对列进行适当的重命名。

为了确定从数据库中检索实体时要实例化的适当类型,应在持久化层次结构的根(且仅在根中)中提供@DiscriminatorColumn注解。

现在让我们看一个简单的例子。 我要让您阅读更多有关 Hiberate 的官方文档中的内容。 我将在以后的文章中详细介绍它们。

//The Root of the Inheritance Hierarchy Mapped with the SINGLE_TABLE Strategy

@Entity
@Inheritance(strategy = SINGLE_TABLE)
@DiscriminatorColumn(
    name="DISCRIMINATOR",
    discriminatorType=INTEGER
)
@DiscriminatorValue("1")
public class Book {
...
}

//A Derived Entity in the Inheritance Hierarchy
@Entity
@DiscriminatorValue("2")
public class ComputerBook extends Book {
...
}

连接表

整体式单表方法的替代方法是其他类似的联合表方法。 此处使用了“区分符”列,但各种派生类型的字段存储在不同的表中。

@Entity
@Inheritance(strategy = JOINED)
@DiscriminatorColumn
    name="DISCRIMINATOR"
)
public class Book {
...
}

每类表

最后,有一种每类表方法,其中继承层次结构中每种类型的所有字段都存储在不同的表中。 由于实体与其表之间的紧密对应关系,因此@DiscriminatorColumn注解不适用于此继承策略。

@Entity
@Inheritance(strategy = TABLE_PER_CLASS)
public class Book {
...
}

其他 JPA2 持久化注解

尽管我们现在涵盖了大多数核心 JPA2 持久化注解,但是您还会经常遇到其他一些注解。 我们将在以下各节中介绍其中一些内容。

@Temporal的时间数据

具有java.util.Datejava.util.Calendar类型的实体的字段或属性表示时间数据。 默认情况下,它们将存储在TIMESTAMP数据类型的列中,但是可以用@Temporal注解覆盖此默认行为。

注解接受javax.persistence.TemporalType枚举的单个值属性。 这提供了三个可能的值:DATETIMETIMESTAMP。 这些分别对应于java.sql.Datejava.sql.Timejava.sql.Timestamp。 在架构生成时为table列提供了适当的数据类型。

@Temporal(TemporalType.TIME)
java.util.Date startingTime;

@ElementCollection的元素集合

除了使用一对多映射来映射集合之外,JPA2 还引入了@ElementCollection注解,用于映射基本或可嵌入类的集合。 您可以使用@ElementCollection注解来简化映射。

@ElementCollection
List<String> passwordHints;

@ElementCollection注解上有两个属性targetClassfetchtargetClass属性告诉 Hibernate 集合中存储了哪个类。 如果在集合上使用泛型,则无需指定targetClass,因为 Hibernate 会推断出正确的类。 fetch属性采用枚举FetchType的成员。 默认情况下为EAGER,但可以将其设置为LAZY以在访问该值时允许加载。

@Lob的大对象

通过应用@Lob注解,可以将持久化属性或字段标记为持久化数据库支持的大对象类型。

注解不带任何属性,但是将从字段或参数的类型中推断出要使用的基础大对象类型。 基于字符串和字符的类型将存储在适当的基于字符的类型中,即 CLOB。 所有其他对象将存储在 BLOB 中。

@Lob
String content; // a very long article

@Lob注解可以与@Basic@ElementCollection注解结合使用。

@MappedSuperclass映射超类

当层次结构的根本身不是持久实体,而是派生自它的各种类时,就会发生继承的特殊情况。 这样的类可以是抽象的或具体的。 @MappedSuperclass注解允许您利用这种情况。

标有@MappedSuperclass的类不是实体,并且不可查询(不能传递给在SessionEntityManager对象中需要实体的方法)。 它不能是关联的目标。

超类列的映射信息将与派生类的详细信息存储在同一表中。

使用@OrderColumn排序集合

尽管@OrderBy允许从数据库中检索数据后进行排序,但 JPA2 还提供了一个注解,该注解允许在数据库中维护适当集合类型(例如List)的排序; 它通过维护代表该顺序的有序列来实现。 这是一个例子:

@OneToMany
@OrderColumn(
   name="employeeNumber"
)
List<Employee> employees;

在这里,我们声明一个employeeNumber列将保持一个值,从 0 开始,并随着每个条目添加到列表中而递增。 默认的起始值可以被 base 属性覆盖。 默认情况下,该列可以包含空(无序)值。 通过将nullable属性设置为false可以覆盖可空性。 默认情况下,从注解生成模式时,该列被假定为整数类型; 但是,可以通过提供指定不同列定义字符串的columnDefinition属性来覆盖此属性。

命名查询(HQL 或 JPQL)

@NamedQuery@NamedQueries

@NamedQuery@NamedQueries允许将一个或多个 Hiberate 查询语言或 Java 持久化查询语言(JPQL)查询与实体相关联。 必需的属性如下:

  1. name是用于检索查询的名称。
  2. query是与名称关联的 JPQL(或 HQL)查询。

以下面的“Author”实体为例。

@Entity
@NamedQuery(
        name="findAuthorsByName",
        query="from Author where name = :author"
)
public class Author {
...
}

该查询将按名称检索Author实体,因此将其与该实体相关联是很自然的。 但是,并没有实际要求以这种方式将命名查询与其所涉及的实体相关联。

您不需要直接将查询与其声明所针对的实体相关联,但是通常这样做。 如果查询与任何实体声明没有自然关联,则可以在包级别进行@NamedQuery注解。

使用@NamedNativeQuery@NamedNativeQueries命名本地查询

@NamedNativeQuery允许您编写命名的 SQL 查询,而@NamedQuery允许您编写命名的 HQL 查询(或 JPQL)。

通常,您应该更喜欢编写 HQL 查询,因为这样您就可以让 Hibernate 处理将 HQL 转换为各种 SQL 方言的复杂过程。 当您选择切换 DBMS 供应器时,这将使您的工作简单得多。

@NamedQueries({
   @NamedQuery(name="get-emp-by-name",query="FROM EmployeeBean WHERE fName=:fName")
})

//Equivalent NamedNativeQuery

@NamedNativeQueries(
	{
		@NamedNativeQuery(
			name="get-emp-by-name-native",
			query="SELECT * FROM Employees WHERE firstName=:fName",
			resultClass=EmployeeEntity.class)
	}
)

简而言之,这就是本篇有限的教程,涵盖了最重要的 JPA2 持久化注解。 我将在以后的教程中详细介绍它们。

祝您学习愉快!