Hibernate.org社区文档

Hibernate 注解

参考指南


首先,设置您的类路径(在您在您喜欢的 IDE 中创建新项目后)

或者,将您的 pom.xml 导入到您喜欢的 IDE 中,并让依赖项自动解析,

建议您使用 Hibernate Validator 和 Bean 验证规范功能,因为它与 Java Persistence 2 的集成已标准化。从 Hibernate 网站下载 Hibernate Validator 4 或更高版本,并将 hibernate-validator.jarvalidation-api.jar 添加到您的类路径中。或者,在您的 pom.xml 中添加以下依赖项。


<project>
  ...
  <dependencies>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>${hibernate-validator-version}</version>
    </dependency>
    ...
  </dependencies>
  ...
</project>

如果您想使用 Hibernate Search,请从 Hibernate 网站下载它,并将 hibernate-search.jar 及其依赖项添加到您的类路径中。或者,在您的 pom.xml 中添加以下依赖项。


<project>
  ...
  <dependencies>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-search</artifactId>
      <version>${hibernate-search-version}</version>
    </dependency>
    ...
  </dependencies>
  ...
</project>

建议您使用 JPA 2 API 启动 Hibernate(有关详细信息,请参阅 Hibernate EntityManager 文档)。如果您使用 Hibernate Core 及其本地 API,请继续阅读。

如果您自己启动 Hibernate,请确保使用 AnnotationConfiguration 类,而不是 Configuration 类。以下是用(传统)HibernateUtil 方法的示例

package hello;


import org.hibernate.*;
import org.hibernate.cfg.*;
import test.*;
import test.animals.Dog;
public class HibernateUtil {
private static final SessionFactory sessionFactory;
    static {
        try {
            sessionFactory = new AnnotationConfiguration()
                    .configure().buildSessionFactory();
        } catch (Throwable ex) {
            // Log exception!
            throw new ExceptionInInitializerError(ex);
        }
    }
    public static Session getSession()
            throws HibernateException {
        return sessionFactory.openSession();
    }
}
            

这里有趣的是使用 AnnotationConfiguration。包和带注解的类在您的常规 XML 配置文件(通常为 hibernate.cfg.xml)中声明。以下是上面声明的等效项


<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
    <mapping package="test.animals"/>
    <mapping class="test.Flight"/>
    <mapping class="test.Sky"/>
    <mapping class="test.Person"/>
    <mapping class="test.animals.Dog"/>

    <mapping resource="test/animals/orm.xml"/>
  </session-factory>
</hibernate-configuration>

请注意,您可以混合使用传统 hbm.xml 和注解方法。资源元素可以是 hbm 文件或 EJB3 XML 部署描述符。对于您的配置过程,这种区别是透明的。

或者,您可以使用程序化 API 定义带注解的类和包

sessionFactory = new AnnotationConfiguration()
                    .addPackage("test.animals") //the fully qualified package name
                    .addAnnotatedClass(Flight.class)
                    .addAnnotatedClass(Sky.class)
                    .addAnnotatedClass(Person.class)
                    .addAnnotatedClass(Dog.class)
                    .addResource("test/animals/orm.xml")

                    .configure()
                    .buildSessionFactory();

除了这种启动例程更改或配置文件更改之外,使用注解时使用 Hibernate API 的方式没有其他区别。您可以使用您喜欢的配置方法来设置其他属性(hibernate.propertieshibernate.cfg.xml、程序化 API 等)。

注意

您可以将带注解的持久类和经典 hbm.cfg.xml 声明与同一个 SessionFactory 混合使用。但是,您不能多次声明一个类(无论是否带注解或通过 hbm.xml)。您也不能在实体层次结构中混合配置策略(hbm 与注解)。

为了简化从 hbm 文件到注解的迁移过程,配置机制会检测注解和 hbm 文件之间的映射重复。然后,HBM 文件在类到类基础上优先于带注解的元数据。您可以使用 hibernate.mapping.precedence 属性更改优先级。默认值为 hbm, class,将其更改为 class, hbm 将在发生冲突时优先考虑带注解的类,而不是 hbm 文件。

Hibernate 注解使用 Java 简单日志门面 (SLF4J) 来记录各种系统事件。SLF4J 可以将您的日志输出定向到多个日志框架(NOP、Simple、log4j 版本 1.2、JDK 1.4 日志记录、JCL 或 logback),具体取决于您选择的绑定。为了正确设置日志记录,您需要在类路径中放置 slf4j-api.jar 以及您喜欢的绑定的 JAR 文件(对于 Log4J 而言,为 slf4j-log4j12.jar)。有关更多详细信息,请参阅 SLF4J 文档

Hibernate 注解感兴趣的日志记录类别为


有关类别配置的更多信息,请参阅 Hibernate Core 文档中的日志记录

JPA 实体是普通 POJO。实际上,它们是 Hibernate 持久性实体。它们的映射是通过 JDK 5.0 注解定义的,而不是 hbm.xml 文件。JPA 2 XML 描述符语法也定义了用于覆盖的语法。注解可以分为两类:逻辑映射注解(描述对象模型、两个实体之间的关联等)和物理映射注解(描述物理模式、表、列、索引等)。在以下代码示例中,我们将混合使用来自这两个类别的注解。

JPA 注解位于 javax.persistence.* 包中。您喜欢的 IDE 可以为您自动完成注解及其属性(即使没有特定的“JPA”模块,因为 JPA 注解是普通的 JDK 5 注解)。

您可以在 Hibernate 注解测试套件本身中找到一个良好而完整的可运行示例集:大多数单元测试都是为了代表一个具体的示例而设计的,并作为您的灵感来源。您可以在发行版中获取测试套件源代码。

每个持久 POJO 类都是一个实体,并使用 @Entity 注解(在类级别)进行声明

@Entity

public class Flight implements Serializable {
    Long id;
    @Id
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
}         

@Entity 将类声明为实体(即持久化 POJO 类),@Id 声明此实体的标识符属性。其他映射声明是隐式的。Flight 类映射到 Flight 表,使用 id 列作为其主键列。

根据您是注解字段还是方法,Hibernate 使用的访问类型将是 fieldproperty。EJB3 规范要求您在将要访问的元素类型上声明注解,即如果您使用 property 访问,则为 getter 方法,如果您使用 field 访问,则为字段。应避免在字段和方法中混合使用注解。Hibernate 将根据 @Id@EmbeddedId 的位置来猜测访问类型。

实体的每个非静态非瞬态属性(根据访问类型为字段或方法)都被认为是持久的,除非您将其注解为 @Transient。属性没有注解等效于相应的 @Basic 注解。@Basic 注解允许您为属性声明获取策略

public transient int counter; //transient property


private String firstname; //persistent property
@Transient
String getLengthInMeter() { ... } //transient property
String getName() {... } // persistent property
@Basic
int getLength() { ... } // persistent property
@Basic(fetch = FetchType.LAZY)
String getDetailedComment() { ... } // persistent property
@Temporal(TemporalType.TIME)
java.util.Date getDepartureTime() { ... } // persistent property           
@Enumerated(EnumType.STRING)
Starred getNote() { ... } //enum persisted as String in database

counter 是一个瞬态字段,lengthInMeter 是一个注解为 @Transient 的方法,实体管理器将忽略它们。 namelengthfirstname 属性被映射为持久且急切获取(简单属性的默认值)。detailedComment 属性值将在首次访问实体的延迟属性时从数据库中延迟获取。通常您不需要延迟简单属性(不要与延迟关联获取混淆)。

推荐的替代方案是使用 JP-QL(Java 持久化查询语言)或 Criteria 查询的投影功能。

JPA 支持 Hibernate 支持的所有基本类型的属性映射(所有基本 Java 类型、它们各自的包装器和可序列化类)。Hibernate 注解开箱即用地支持枚举类型映射,要么映射到序数列(保存枚举序数),要么映射到基于字符串的列(保存枚举字符串表示):默认情况下为序数的持久化表示可以通过 @Enumerated 注解覆盖,如 note 属性示例所示。

在纯 Java API 中,时间的精度未定义。在处理时间数据时,您可能希望在数据库中描述预期的精度。时间数据可以具有 DATETIMETIMESTAMP 精度(即实际日期、仅时间或两者)。使用 @Temporal 注解来微调这一点。

@Lob 指示属性应持久化到 Blob 或 Clob 中,具体取决于属性类型:java.sql.ClobCharacter[]char[] 和 java.lang.String 将持久化到 Clob 中。java.sql.BlobByte[]byte[] 和可序列化类型将持久化到 Blob 中。



@Lob
public String getFullText() {
    return fullText;
}
@Lob 
public byte[] getFullCode() {
    return fullCode;
}
 

如果属性类型实现 java.io.Serializable 并且不是基本类型,并且如果属性没有用 @Lob 注解,那么将使用 Hibernate serializable 类型。

默认情况下,类层次结构的访问类型由 @Id@EmbeddedId 注解的位置定义。如果这些注解位于字段上,则仅字段被视为持久化,并且状态通过字段访问。如果这些注解位于 getter 上,则仅 getter 被视为持久化,并且状态通过 getter/setter 访问。这在实践中运作良好,是推荐的方法。

但是,在某些情况下,您需要

最好的用例是多个实体使用的可嵌入类,这些实体可能不使用相同的访问类型。在这种情况下,最好在可嵌入类级别强制访问类型。

要强制在给定类上使用访问类型,请使用 @Access 注解,如下所示

@Entity

public class Order {
   @Id private Long id;
   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }
   @Embedded private Address address;
   public Address getAddress() { return address; }
   public void setAddress() { this.address = address; }
}
@Entity
public class User {
   private Long id;
   @Id public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }
   private Address address;
   @Embedded public Address getAddress() { return address; }
   public void setAddress() { this.address = address; }
}
@Embeddable
@Access(AcessType.PROPERTY)
public class Address {
   private String street1;
   public String getStreet1() { return street1; }
   public void setStreet1() { this.street1 = street1; }
   private hashCode; //not persistent
}

您还可以覆盖单个属性的访问类型,同时保持其他属性的标准。

@Entity

public class Order {
   @Id private Long id;
   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }
   @Transient private String userId;
   @Transient private String orderId;
   @Access(AccessType.PROPERTY)
   public String getOrderNumber() { return userId + ":" + orderId; }
   public void setOrderNumber() { this.userId = ...; this.orderId = ...; }
}

在此示例中,默认访问类型为 FIELD,但 orderNumber 属性除外。请注意,相应的字段(如果有)必须标记为 @Transienttransient

可以使用 @Column 注解定义用于属性映射的列。使用它来覆盖默认值(有关默认值的更多信息,请参阅 EJB3 规范)。您可以将此注解应用于属性级别,适用于以下属性:



@Entity
public class Flight implements Serializable {
...
@Column(updatable = false, name = "flight_name", nullable = false, length=50)
public String getName() { ... }
            

name 属性映射到 flight_name 列,该列不可为空,长度为 50 且不可更新(使属性不可变)。

此注解可以应用于常规属性以及 @Id@Version 属性。

@Column(
    name="colu(1)mnName";
    boolean un(2)ique() default false;
    boolean nu(3)llable() default true;
    boolean in(4)sertable() default true;
    boolean up(5)datable() default true;
    String col(6)umnDefinition() default "";
    String tab(7)le() default "";
    int length(8)() default 255;
    int precis(9)ion() default 0; // decimal precision
    int scale((10)) default 0; // decimal scale

1

name(可选):列名(默认为属性名)

2

unique(可选):在该列上设置唯一约束与否(默认为 false)

3

nullable(可选):将列设置为可为空(默认为 true)。

4

insertable(可选):列是否将是 insert 语句的一部分(默认为 true)

5

updatable(可选):列是否将是 update 语句的一部分(默认为 true)

6

columnDefinition(可选):覆盖此特定列的 SQL DDL 片段(不可移植)

7

table(可选):定义目标表(默认为主表)

8

length(可选):列长度(默认为 255)

8

precision(可选):列小数精度(默认为 0)

10

scale(可选):如果有用,列小数位数(默认为 0)

可以在实体内声明嵌入组件,甚至可以覆盖其列映射。组件类必须在类级别用 @Embeddable 注解进行注解。可以使用 @Embedded@AttributeOverride 注解在关联属性中覆盖特定实体的嵌入对象的列映射

@Entity

public class Person implements Serializable {
    // Persistent component using defaults
    Address homeAddress;
    @Embedded
    @AttributeOverrides( {
            @AttributeOverride(name="iso2", column = @Column(name="bornIso2") ),
            @AttributeOverride(name="name", column = @Column(name="bornCountryName") )
    } )
    Country bornIn;
    ...
}          
@Embeddable

public class Address implements Serializable {
    String city;
    Country nationality; //no overriding here
}            
@Embeddable

public class Country implements Serializable {
    private String iso2;
    @Column(name="countryName") private String name;
    public String getIso2() { return iso2; }
    public void setIso2(String iso2) { this.iso2 = iso2; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    ...
}            

可嵌入对象继承其拥有实体的访问类型(请注意,您可以使用 @Access 注解覆盖它)。

Person 实体具有两个组件属性,homeAddressbornInhomeAddress 属性没有被注解,但 Hibernate 将通过查找 Address 类中的 @Embeddable 注解来猜测它是一个持久组件。我们还使用 @Embedded@AttributeOverride 注解为 Country 的每个映射属性覆盖列名的映射(到 bornCountryName)。如您所见,Country 也是 Address 的嵌套组件,同样使用 Hibernate 和 JPA 默认值的自动检测。通过点表达式覆盖嵌入对象的嵌入对象的列。

    @Embedded

    @AttributeOverrides( {
            @AttributeOverride(name="city", column = @Column(name="fld_city") ),
            @AttributeOverride(name="nationality.iso2", column = @Column(name="nat_Iso2") ),
            @AttributeOverride(name="nationality.name", column = @Column(name="nat_CountryName") )
            //nationality columns in homeAddress are overridden
    } )
    Address homeAddress;

Hibernate 注解支持 JPA 规范中未明确支持的功能。您可以使用 @MappedSuperclass 注解来注释嵌入对象,使超类属性持久化(有关更多信息,请参见 @MappedSuperclass)。

您也可以在嵌入式对象中使用关联注解(例如 @OneToOne@ManyToOne@OneToMany@ManyToMany)。要覆盖关联列,可以使用 @AssociationOverride

如果您想在同一个实体中两次使用相同的嵌入对象类型,列名称默认机制将不起作用,因为多个嵌入对象将共享同一组列。在纯 JPA 中,您需要覆盖至少一组列。但是,Hibernate 允许您通过 NamingStrategy 接口来增强默认命名机制。您可以编写一个策略来防止在这种情况下发生名称冲突。 DefaultComponentSafeNamingStrategy 就是一个例子。

@Id 注解允许您定义哪个属性是实体的标识符。此属性可以由应用程序本身设置,也可以由 Hibernate 生成(首选)。您可以通过 @GeneratedValue 注解来定义标识符生成策略。

JPA 定义了五种标识符生成策略类型

Hibernate 提供了比基本 JPA 更多的 id 生成器。有关更多信息,请查看 第 2.4 节“Hibernate 注解扩展”

以下示例演示了使用 SEQ_STORE 配置的序列生成器(见下文)

@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE")

public Integer getId() { ... }         

下一个示例使用身份生成器

@Id @GeneratedValue(strategy=GenerationType.IDENTITY)

public Long getId() { ... }         

AUTO 生成器是可移植应用程序(跨多个数据库供应商)的首选类型。标识符生成配置可以通过生成器属性为多个 @Id 映射共享。可以通过 @SequenceGenerator@TableGenerator 使用几种配置。生成器的范围可以是应用程序或类。类定义的生成器在类外部不可见,可以覆盖应用程序级生成器。应用程序级生成器在 XML 级别定义(请参见 第 3 章“通过 XML 覆盖元数据”)。

<table-generator name="EMP_GEN"

            table="GENERATOR_TABLE"
            pk-column-name="key"
            value-column-name="hi"
            pk-column-value="EMP"
            allocation-size="20"/>
//and the annotation equivalent
@javax.persistence.TableGenerator(
    name="EMP_GEN",
    table="GENERATOR_TABLE",
    pkColumnName = "key",
    valueColumnName = "hi"
    pkColumnValue="EMP",
    allocationSize=20
)
<sequence-generator name="SEQ_GEN" 
    sequence-name="my_sequence"
    allocation-size="20"/>
//and the annotation equivalent
@javax.persistence.SequenceGenerator(
    name="SEQ_GEN",
    sequenceName="my_sequence",
    allocationSize=20
)
         

如果 JPA XML(如 META-INF/orm.xml)用于定义生成器,则 EMP_GENSEQ_GEN 是应用程序级生成器。 EMP_GEN 定义了一个基于表的 id 生成器,使用 hilo 算法,max_lo 为 20。hi 值保存在 table "GENERATOR_TABLE" 中。信息保存在一个 pkColumnName "key" 等于 pkColumnValue "EMP" 的行中,列 valueColumnName "hi" 包含下一个使用的 hi 值。

SEQ_GEN 定义了一个使用名为 my_sequence 的序列的序列生成器。此基于序列的 hilo 算法使用的分配大小为 20。请注意,此版本的 Hibernate 注解不处理序列生成器中的 initialValue。默认分配大小为 50,因此如果您想使用序列并在每次获取值,则必须将分配大小设置为 1。

重要

我们建议所有新项目使用 hibernate.id.new_generator_mappings=true,因为新的生成器更高效,更接近 JPA 2 规范语义。但是它们与现有数据库不向后兼容(如果使用序列或表进行 id 生成)。有关如何激活它们的更多信息,请参见 第 1.3 节“属性”

注意

JPA 规范不支持包级别定义。但是,您可以在包级别使用 @GenericGenerator(请参见 第 2.4.2 节“标识符”)。

以下示例显示了在类范围内定义序列生成器

@Entity

@javax.persistence.SequenceGenerator(
    name="SEQ_STORE",
    sequenceName="my_sequence"
)
public class Store implements Serializable {
    private Long id;
    @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE")
    public Long getId() { return id; }
}         

此类将使用名为 my_sequence 的序列,而 SEQ_STORE 生成器在其他类中不可见。请注意,您可以在 org.hibernate.test.annotations.id 包中的 Hibernate 注解测试中找到更多示例。

最后,您可以让 Hibernate 从另一个关联实体复制标识符。在 Hibernate 行话中,它被称为外键生成器,但 JPA 映射更易读,建议使用。

@Entity

class MedicalHistory implements Serializable {
  @Id @OneToOne
  @JoinColumn(name = "person_id")
  Person patient;
}
@Entity
public class Person implements Serializable {
  @Id @GeneratedValue Integer id;
}

或者

@Entity

class MedicalHistory implements Serializable {
  @Id Integer id;
  @MapsId @OneToOne
  @JoinColumn(name = "patient_id")
  Person patient;
}
@Entity
class Person {
  @Id @GeneratedValue Integer id;
}

如果您对更多“派生身份”的示例感兴趣,JPA 2 规范在第 2.4.1.3 章中提供了一组很好的示例。

但标识符不必是单个属性,它可以由多个属性组成。

您可以通过几种语法定义复合主键

如您所见,最后一种情况远非显而易见。它继承自 EJB 2 的黑暗时代,用于向后兼容性,我们建议您不要使用它(为了简单起见)。

让我们使用示例探索所有三种情况。

这是一个 @EmbeddedId 的简单示例。

@Entity

class User {
  @EmbeddedId
  @AttributeOverride(name="firstName", column=@Column(name="fld_firstname")
  UserId id;
  Integer age;
}
@Embeddable
class UserId implements Serializable {
  String firstName;
  String lastName;
}

您可以注意到 UserId 类是可序列化的。要覆盖列映射,请使用 @AttributeOverride

嵌入式 id 本身可以包含关联实体的主键。

@Entity

class Customer {
  @EmbeddedId CustomerId id;
  boolean preferredCustomer;
  @MapsId("userId")
  @JoinColumns({
    @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
    @JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
  })
  @OneToOne User user;
}
@Embeddable
class CustomerId implements Serializable {
  UserId userId;
  String customerNumber;
}
@Entity 
class User {
  @EmbeddedId UserId id;
  Integer age;
}
@Embeddable
class UserId implements Serializable {
  String firstName;
  String lastName;
}

在嵌入式 id 对象中,关联表示为关联实体的标识符。但您可以通过 @MapsId 注解将它的值链接到实体中的常规关联。 @MapsId 值对应于包含关联实体标识符的嵌入式 id 对象的属性名称。在数据库中,这意味着 Customer.userCustomerId.userId 属性共享相同的底层列(在本例中为 user_fk)。

在实践中,您的代码只设置 Customer.user 属性,Hibernate 将用户 id 值复制到 CustomerId.userId 属性中。

虽然 JPA 中不支持,但 Hibernate 允许您将关联直接放在嵌入式 id 组件中(而不是使用 @MapsId 注解)。

@Entity

class Customer {
  @EmbeddedId CustomerId id;
  boolean preferredCustomer;
}
@Embeddable
class CustomerId implements Serializable {
  @OneToOne
  @JoinColumns({
    @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
    @JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
  }) 
  User user;
  String customerNumber;
}
@Entity 
class User {
  @EmbeddedId UserId id;
  Integer age;
}
@Embeddable
class UserId implements Serializable {
  String firstName;
  String lastName;
}

@IdClass 在实体上指向表示类标识符的类(组件)。实体上标有 @Id 的属性必须在其 @IdClass 上有对应的属性。搜索双重属性的返回值必须相同,对于基本属性必须相同,或者对于关联必须对应于关联实体的标识符类。

@Entity

class Customer {
  @Id @OneToOne
  @JoinColumns({
    @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
    @JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
  }) 
  User user;
  
  @Id String customerNumber;
  boolean preferredCustomer;
}
class CustomerId implements Serializable {
  UserId user;
  String customerNumber;
}
@Entity 
class User {
  @EmbeddedId UserId id;
  Integer age;
}
@Embeddable
class UserId implements Serializable {
  String firstName;
  String lastName;
}

CustomerCustomerId 具有相同的属性 customerNumber 以及 user

虽然不是 JPA 标准,但 Hibernate 允许您在 @IdClass 中声明普通关联属性。

@Entity

class Customer {
  @Id @OneToOne
  @JoinColumns({
    @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
    @JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
  }) 
  User user;
  
  @Id String customerNumber;
  boolean preferredCustomer;
}
class CustomerId implements Serializable {
  @OneToOne User user;
  String customerNumber;
}
@Entity 
class User {
  @EmbeddedId UserId id;
  Integer age;
}
@Embeddable
class UserId implements Serializable {
  String firstName;
  String lastName;
}

EJB3 支持三种类型的继承

所选策略是在层次结构中顶级实体的类级别使用 @Inheritance 注解声明的。

这有时很有用,可以 通过技术或业务超类共享公共属性,而无需将其作为常规映射实体(即此实体没有特定的表)包含在内。为此,您可以将它们映射为 @MappedSuperclass

@MappedSuperclass

public class BaseEntity {
    @Basic
    @Temporal(TemporalType.TIMESTAMP)
    public Date getLastUpdate() { ... }
    public String getLastUpdater() { ... }
    ...
}
@Entity class Order extends BaseEntity {
    @Id public Integer getId() { ... }
    ...
}

在数据库中,此层次结构将表示为一个 Order 表,其中包含 idlastUpdatelastUpdater 列。嵌入式超类属性映射会被复制到它们的实体子类中。请记住,嵌入式超类并非层次结构的根。

您可以使用 @AttributeOverride 注解,在根实体级别覆盖在实体超类中定义的列。

@MappedSuperclass

public class FlyingObject implements Serializable {
    public int getAltitude() {
        return altitude;
    }
    @Transient
    public int getMetricAltitude() {
        return metricAltitude;
    }
    @ManyToOne
    public PropulsionType getPropulsion() {
        return metricAltitude;
    }
    ...
}
@Entity
@AttributeOverride( name="altitude", column = @Column(name="fld_altitude") )
@AssociationOverride( 
   name="propulsion", 
   joinColumns = @JoinColumn(name="fld_propulsion_fk") 
)
public class Plane extends FlyingObject {
    ...
}

altitude 属性将持久化到 Plane 表的 fld_altitude 列中,而推进关联将通过 fld_propulsion_fk 外键列进行物化。

您可以在 @Entity 类、@MappedSuperclass 类以及指向 @Embeddable 对象的属性上定义 @AttributeOverride@AssociationOverride

您可以使用 @OneToOne 通过一对一关系关联实体。一对一关联有三种情况:关联的实体共享相同的主键值,一个实体持有外键(请注意,数据库中的此 FK 列应该被约束为唯一,以模拟一对一的多重性),或者使用关联表来存储两个实体之间的链接(必须对每个 fk 定义一个唯一约束,以确保一对一的多重性)。

首先,我们使用共享主键来映射一个真正的一对一关联。

@Entity

public class Body {
    @Id
    public Long getId() { return id; }
    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    public Heart getHeart() {
        return heart;
    }
    ...
}            
@Entity

public class Heart {
    @Id
    public Long getId() { ...}
}            

@PrimaryKeyJoinColumn 注解表示实体的主键被用作关联实体的外键值。

在下面的示例中,关联的实体通过显式外键列链接。

@Entity

public class Customer implements Serializable {
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="passport_fk")
    public Passport getPassport() {
        ...
    }
@Entity
public class Passport implements Serializable {
    @OneToOne(mappedBy = "passport")
    public Customer getOwner() {
    ...
}            

一个 Customer 与一个 Passport 链接,在 Customer 表中有一个名为 passport_fk 的外键列。连接列通过 @JoinColumn 注解声明,该注解类似于 @Column 注解。它还有一个名为 referencedColumnName 的参数。此参数声明目标实体中将用于连接的列。请注意,当将 referencedColumnName 用于非主键列时,关联类必须是 Serializable。还要注意,将 referencedColumnName 用于非主键列必须映射到具有单个列的属性(其他情况可能无法正常工作)。

关联可以是双向的。在双向关系中,其中一侧(且只有一侧)必须是所有者:所有者负责关联列的更新。要将一侧声明为 负责关系,则使用属性 mappedBymappedBy 指的是所有者侧关联的属性名称。在本例中,它是 passport。如您所见,您不必(也不应该)声明连接列,因为它已经在所有者侧声明过了。

如果在所有者侧没有声明 @JoinColumn,则将应用默认值。将在所有者表中创建连接列,其名称将是所有者侧关系名称、_(下划线)以及被拥有侧主键列名称的串联。在本例中为 passport_id,因为属性名称是 passport,而 Passport 的 id 列是 id

第三种可能性(使用关联表)相当特殊。

@Entity

public class Customer implements Serializable {
    @OneToOne(cascade = CascadeType.ALL)
    @JoinTable(name = "CustomerPassports",
        joinColumns = @JoinColumn(name="customer_fk"),
        inverseJoinColumns = @JoinColumn(name="passport_fk")
    )
    public Passport getPassport() {
        ...
    }
@Entity
public class Passport implements Serializable {
    @OneToOne(mappedBy = "passport")
    public Customer getOwner() {
    ...
}          

一个 Customer 通过一个名为 CustomerPassports 的关联表与一个 Passport 链接;此关联表有一个名为 passport_fk 的外键列,指向 Passport 表(由 inverseJoinColumn 物化),以及一个名为 customer_fk 的外键列,指向 Customer 表(由 joinColumns 属性物化)。

您必须在这样的映射中明确声明连接表名称和连接列。

多对一关联是在属性级别使用 @ManyToOne 注解声明的。

@Entity()

public class Flight implements Serializable {
    @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
    @JoinColumn(name="COMP_ID")
    public Company getCompany() {
        return company;
    }
    ...
}            

@JoinColumn 属性是可选的,默认值为一对一中的值,即所有者侧关系名称、_(下划线)以及被拥有侧主键列名称的串联。在本例中为 company_id,因为属性名称是 company,而 Company 的 id 列是 id

@ManyToOne 具有一个名为 targetEntity 的参数,它描述了目标实体名称。您通常不需要此参数,因为默认值(存储关联的属性类型)在几乎所有情况下都适用。但是,当您想使用接口作为返回类型而不是常规实体时,这很有用。

@Entity

public class Flight implements Serializable {
    @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity=CompanyImpl.class )
    @JoinColumn(name="COMP_ID")
    public Company getCompany() {
        return company;
    }
    ...
}
public interface Company {
    ...
}

您也可以通过关联表来映射多对一关联。此关联表由 @JoinTable 注解描述,将包含一个引用回实体表的外键(通过 @JoinTable.joinColumns)和一个引用目标实体表的外键(通过 @JoinTable.inverseJoinColumns)。

@Entity

public class Flight implements Serializable {
    @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
    @JoinTable(name="Flight_Company",
        joinColumns = @JoinColumn(name="FLIGHT_ID"),
        inverseJoinColumns = @JoinColumn(name="COMP_ID")
    )
    public Company getCompany() {
        return company;
    }
    ...
}       

您可以将指向关联实体的 CollectionListMapSet 映射为一对多或多对多关联,分别使用 @OneToMany@ManyToMany 注解。如果集合是基本类型或嵌入式类型,请使用 @ElementCollection。我们将在以下各节中对此进行更详细的描述。

一对多关联是在属性级别使用 @OneToMany 注解声明的。一对多关联可以是双向的。

多对多关联使用 @ManyToMany 注解在逻辑上定义。您还需要使用 @JoinTable 注解描述关联表和连接条件。如果关联是双向的,则一侧必须是所有者,另一侧必须是反向端(即,在更新关联表中的关系值时将忽略它)。

@Entity

public class Employer implements Serializable {
    @ManyToMany(
        targetEntity=org.hibernate.test.metadata.manytomany.Employee.class,
        cascade={CascadeType.PERSIST, CascadeType.MERGE}
    )
    @JoinTable(
        name="EMPLOYER_EMPLOYEE",
        joinColumns=@JoinColumn(name="EMPER_ID"),
        inverseJoinColumns=@JoinColumn(name="EMPEE_ID")
    )
    public Collection getEmployees() {
        return employees;
    }
    ...
}               
@Entity

public class Employee implements Serializable {
    @ManyToMany(
        cascade = {CascadeType.PERSIST, CascadeType.MERGE},
        mappedBy = "employees",
        targetEntity = Employer.class
    )
    public Collection getEmployers() {
        return employers;
    }
}               

我们已经展示了关联的许多声明和详细属性。我们将深入探讨 @JoinTable 描述,它定义了一个 name、一个连接列数组(注解中的数组使用 { A, B, C } 定义)以及一个反向连接列数组。后面的列是关联表的列,这些列引用 Employee 主键(“另一侧”)。

如前所述,另一侧不必(不应)描述物理映射:一个简单的 mappedBy 参数包含所有者端属性名称,将两者绑定在一起。

与其他任何注解一样,大多数值在多对多关系中都是猜测出来的。在没有描述单向多对多的任何物理映射的情况下,将应用以下规则。表名称是所有者表名称、_ 和另一侧表名称的串联。引用所有者表的外部键名称是所有者表名称、_ 和所有者主键列的串联。引用另一侧的外部键名称是所有者属性名称、_ 和另一侧主键列的串联。这些规则与单向一对多关系使用的规则相同。



@Entity
public class Store {
    @ManyToMany(cascade = CascadeType.PERSIST)
    public Set<City> getImplantedIn() {
        ...
    }
}
@Entity
public class City {
    ... //no bidirectional relationship
}
               

一个 Store_City 用作连接表。 Store_id 列是 Store 表的外键。 implantedIn_id 列是 City 表的外键。

在没有描述双向多对多的任何物理映射的情况下,将应用以下规则。表名称是所有者表名称、_ 和另一侧表名称的串联。引用所有者表的外部键名称是另一侧属性名称、_ 和所有者主键列的串联。引用另一侧的外部键名称是所有者属性名称、_ 和另一侧主键列的串联。这些规则与单向一对多关系使用的规则相同。

@Entity

public class Store {
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    public Set<Customer> getCustomers() {
        ...
    }
}
@Entity
public class Customer {
    @ManyToMany(mappedBy="customers")
    public Set<Store> getStores() {
        ...
    }
}               

一个 Store_Customer 用作连接表。 stores_id 列是 Store 表的外键。 customers_id 列是 Customer 表的外键。

在一些简单的情况下,不需要关联两个实体,而是简单地创建一个基本类型或嵌入式对象的集合。在这种情况下,使用 @ElementCollection

@Entity

public class User {
   [...]
   public String getLastname() { ...}
   @ElementCollection
   @CollectionTable(name="Nicknames", joinColumns=@JoinColumn(name="user_id"))
   @Column(name="nickname")
   public Set<String> getNicknames() { ... } 
}

使用 @CollectionTable 注解设置保存集合数据的集合表。如果省略,集合表名称默认为包含实体的名称和集合属性的名称的串联,用下划线隔开:在我们的示例中,它将是 User_nicknames

使用 @Column 注解设置保存基本类型的列。如果省略,列名称默认为属性名称:在我们的示例中,它将是 nicknames

但是您不仅限于基本类型,集合类型可以是任何嵌入式对象。要覆盖集合表中嵌入式对象的列,请使用 @AttributeOverride 注解。

@Entity

public class User {
   [...]
   public String getLastname() { ...}
   @ElementCollection
   @CollectionTable(name="Addresses", joinColumns=@JoinColumn(name="user_id"))
   @AttributeOverrides({
      @AttributeOverride(name="street1", column=@Column(name="fld_street"))
   })
   public Set<Address> getAddresses() { ... } 
}
@Embeddable
public class Address {
   public String getStreet1() {...}
   [...]
}

此类嵌入式对象本身不能包含集合。

列表可以通过两种不同的方式进行映射

要在内存中对列表进行排序,请在您的属性中添加 @javax.persistence.OrderBy。此注解将以逗号分隔的属性列表(目标实体的属性)作为参数,并相应地对集合进行排序(例如 firstname asc, age desc),如果字符串为空,则集合将按目标实体的主键进行排序。

@Entity

public class Customer {
   @Id @GeneratedValue public Integer getId() { return id; }
   public void setId(Integer id) { this.id = id; }
   private Integer id;
   @OneToMany(mappedBy="customer")
   @OrderBy("number")
   public List<Order> getOrders() { return orders; }
   public void setOrders(List<Order> orders) { this.orders = orders; }
   private List<Order> orders;
}
@Entity
public class Order {
   @Id @GeneratedValue public Integer getId() { return id; }
   public void setId(Integer id) { this.id = id; }
   private Integer id;
   public String getNumber() { return number; }
   public void setNumber(String number) { this.number = number; }
   private String number;
   @ManyToOne
   public Customer getCustomer() { return customer; }
   public void setCustomer(Customer customer) { this.customer = customer; }
   private Customer number;
}
-- Table schema
|-------------| |----------|
| Order       | | Customer |
|-------------| |----------|
| id          | | id       |
| number      | |----------| 
| customer_id |
|-------------|

要将索引值存储在专用列中,请在您的属性上使用 @javax.persistence.OrderColumn 注解。此注解描述了保存索引值的列的列名称和属性。此列位于包含关联外键的表中。如果未指定列名称,则默认值为引用属性的名称,后跟下划线,后跟 ORDER(在以下示例中,它将是 orders_ORDER)。

@Entity

public class Customer {
   @Id @GeneratedValue public Integer getId() { return id; }
   public void setId(Integer id) { this.id = id; }
   private Integer id;
   @OneToMany(mappedBy="customer")
   @OrderColumn(name"orders_index")
   public List<Order> getOrders() { return orders; }
   public void setOrders(List<Order> orders) { this.orders = orders; }
   private List<Order> orders;
}
@Entity
public class Order {
   @Id @GeneratedValue public Integer getId() { return id; }
   public void setId(Integer id) { this.id = id; }
   private Integer id;
   public String getNumber() { return number; }
   public void setNumber(String number) { this.number = number; }
   private String number;
   @ManyToOne
   public Customer getCustomer() { return customer; }
   public void setCustomer(Customer customer) { this.customer = customer; }
   private Customer number;
}
-- Table schema
|--------------| |----------|
| Order        | | Customer |
|--------------| |----------|
| id           | | id       |
| number       | |----------| 
| customer_id  |
| orders_index |
|--------------|

同样,映射可以从关联实体属性之一借用其键,也可以有专用的列来存储显式键。

要使用目标实体属性之一作为映射的键,请使用 @MapKey(name="myProperty")myProperty 是目标实体中的属性名称)。当使用 @MapKey(没有属性名称)时,将使用目标实体主键。映射键使用与指向的属性相同的列:没有定义额外的列来保存映射键,这很有道理,因为映射键实际上代表一个目标属性。请注意,加载后,键将不再与属性保持同步,换句话说,如果您更改属性值,键不会在您的 Java 模型中自动更改。

@Entity

public class Customer {
   @Id @GeneratedValue public Integer getId() { return id; }
   public void setId(Integer id) { this.id = id; }
   private Integer id;
   @OneToMany(mappedBy="customer")
   @MapKey(name"number")
   public Map<String,Order> getOrders() { return orders; }
   public void setOrders(Map<String,Order> order) { this.orders = orders; }
   private Map<String,Order> orders;
}
@Entity
public class Order {
   @Id @GeneratedValue public Integer getId() { return id; }
   public void setId(Integer id) { this.id = id; }
   private Integer id;
   public String getNumber() { return number; }
   public void setNumber(String number) { this.number = number; }
   private String number;
   @ManyToOne
   public Customer getCustomer() { return customer; }
   public void setCustomer(Customer customer) { this.customer = customer; }
   private Customer number;
}
-- Table schema
|-------------| |----------|
| Order       | | Customer |
|-------------| |----------|
| id          | | id       |
| number      | |----------| 
| customer_id |
|-------------|

否则,映射键将映射到一个或多个专用列。要自定义设置,请使用以下注解之一

您还可以使用 @MapKeyClass 定义键的类型,如果您不使用泛型(在这个阶段,您应该想知道为什么在今天这个时代您不使用泛型)。

@Entity

public class Customer {
   @Id @GeneratedValue public Integer getId() { return id; }
   public void setId(Integer id) { this.id = id; }
   private Integer id;
   @OneToMany @JoinTable(name="Cust_Order")
   @MapKeyColumn(name"orders_number")
   public Map<String,Order> getOrders() { return orders; }
   public void setOrders(Map<String,Order> orders) { this.orders = orders; }
   private Map<String,Order> orders;
}
@Entity
public class Order {
   @Id @GeneratedValue public Integer getId() { return id; }
   public void setId(Integer id) { this.id = id; }
   private Integer id;
   public String getNumber() { return number; }
   public void setNumber(String number) { this.number = number; }
   private String number;
   @ManyToOne
   public Customer getCustomer() { return customer; }
   public void setCustomer(Customer customer) { this.customer = customer; }
   private Customer number;
}
-- Table schema
|-------------| |----------| |---------------|
| Order       | | Customer | | Cust_Order    |
|-------------| |----------| |---------------|
| id          | | id       | | customer_id   |
| number      | |----------| | order_id      |
| customer_id |              | orders_number |
|-------------|              |---------------|

现在让我们根据您选择的映射来探索各种集合语义。


具体来说,没有 @OrderColumn 或 @IndexColumn 的 java.util.List 集合将被视为包。

通过 Hibernate 特定的扩展可以获得更多对集合的支持(请参阅 第 2.4 节,“Hibernate 注解扩展”)。

您可能已经注意到 cascade 属性接受 CascadeType 数组作为值。JPA 中的级联概念与 Hibernate 中的操作的传递持久性和级联非常相似,但语义和级联类型略有不同

有关级联和创建/合并语义的更多信息,请参阅 JPA 规范的第 6.3 章。

您还可以启用孤儿删除语义。如果从 @OneToMany 集合中删除一个实体,或者从 @OneToOne 关联中解除对关联实体的引用,则如果 orphanRemoval 设置为 true,则此关联实体可以被标记为要删除。在某种程度上,这意味着关联实体的生命周期绑定到拥有实体,就像嵌入式对象一样。

@Entity class Customer {

   @OneToMany(orphanRemoval=true) public Set<Order> getOrders() { return orders; }
   public void setOrders(Set<Order> orders) { this.orders = orders; }
   private Set<Order> orders;
   [...]
}
@Entity class Order { ... }
Customer customer = em.find(Customer.class, 1l);
Order order = em.find(Order.class, 1l);
customer.getOrders().remove(order); //order will be deleted by cascade

复合主键使用嵌入式类作为主键表示,因此您需要使用 @Id@Embeddable 注解。或者,您可以使用 @EmbeddedId 注解。请注意,依赖类必须是可序列化的,并实现 equals()/hashCode()。您也可以使用 @IdClass。这些内容将在 第 2.2.3 节“映射标识符属性” 中详细介绍。

@Entity

public class RegionalArticle implements Serializable {
    @Id
    public RegionalArticlePk getPk() { ... }
}
@Embeddable
public class RegionalArticlePk implements Serializable { ... }       

或者

@Entity

public class RegionalArticle implements Serializable {
    @EmbeddedId
    public RegionalArticlePk getPk() { ... }
}
public class RegionalArticlePk implements Serializable { ... }         

@Embeddable 继承其拥有实体的访问类型,除非使用 @Access。复合外键(如果未使用默认敏感值)是在使用 @JoinColumns 元素的关联关系中定义的,它本质上是一个 @JoinColumn 数组。建议明确表达 referencedColumnNames。否则,Hibernate 将假设您使用与主键声明中相同的列顺序。

@Entity

public class Parent implements Serializable {
    @Id
    public ParentPk id;
    public int age;
    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumns ({
        @JoinColumn(name="parentCivility", referencedColumnName = "isMale"),
        @JoinColumn(name="parentLastName", referencedColumnName = "lastName"),
        @JoinColumn(name="parentFirstName", referencedColumnName = "firstName")
    })
    public Set<Child> children; //unidirectional
    ...
} 
@Entity

public class Child implements Serializable {
    @Id @GeneratedValue
    public Integer id;
    @ManyToOne
    @JoinColumns ({
        @JoinColumn(name="parentCivility", referencedColumnName = "isMale"),
        @JoinColumn(name="parentLastName", referencedColumnName = "lastName"),
        @JoinColumn(name="parentFirstName", referencedColumnName = "firstName")
    })
    public Parent parent; //unidirectional
}       
@Embeddable

public class ParentPk implements Serializable {
    String firstName;
    String lastName;
    ...
}        

注意对 referencedColumnName 的显式使用。

Hibernate 自然地为实体提供了一级缓存,称为持久化上下文,通过 Session 的概念实现。此缓存是当前用例的上下文。但是,某些实体由许多不同的用例共享,并且很少更改。您可以将这些实体缓存到所谓的二级缓存中。

默认情况下,实体不是二级缓存的一部分。虽然我们不推荐这样做,但您可以通过在您的 persistence.xml 文件中设置 shared-cache-mode 元素或使用 javax.persistence.sharedCache.mode 属性 来覆盖此行为。以下值是可能的

可以使用 hibernate.cache.default_cache_concurrency_strategy 属性设置默认使用的缓存并发策略

@Entity @Cacheable

@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Forest { ... }

Hibernate 还允许您缓存集合的内容或标识符,如果集合包含其他实体。在集合属性上使用 @Cache 注解。

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)

@JoinColumn(name="CUST_ID")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public SortedSet<Ticket> getTickets() {
    return tickets;
}

@org.hibernate.annotations.Cache 定义给定二级缓存的缓存策略和区域。

@Cache(
    CacheConcu(1)rrencyStrategy usage();
    String reg(2)ion() default "";
    String inc(3)lude() default "all";
)

1

用法:给定的缓存并发策略(NONE、READ_ONLY、NONSTRICT_READ_WRITE、READ_WRITE、TRANSACTIONAL)

2

区域(可选):缓存区域(默认为类的全限定名或集合的全限定名角色名)。

3

include(可选):all 表示包含所有属性,non-lazy 表示仅包含非延迟属性(默认值为 all)。

虽然您可以在代码中编写查询,但建议将它们外部化

不幸的是,您将失去使用 Criteria API 编写的查询的类型安全性。

您可以使用注解映射 JP-QL/HQL 查询。 @NamedQuery@NamedQueries 可以在类级别或 JPA XML 部署描述符中定义。但是它们的定义对于会话工厂/实体管理器工厂范围是全局的。命名查询由其名称和实际查询字符串定义。

<entity-mappings>

    <named-query name="plane.getAll">
        <query>select p from Plane p</query>
    </named-query>
    ...
</entity-mappings>
...
@Entity
@NamedQuery(name="night.moreRecentThan", query="select n from Night n where n.date >= :date")
public class Night {
    ...
}
public class MyDao {
    doStuff() {
        Query q = s.getNamedQuery("night.moreRecentThan");
        q.setDate( "date", aMonthAgo );
        List results = q.list();
        ...
    }
    ...
}      

您还可以通过 hints 属性提供一些提示,该属性是一个 QueryHint 数组。

可用的 Hibernate 提示是


您还可以使用 lockMode 属性定义返回的实体应以何种锁定模式锁定。这等效于实体管理器查找操作的可选锁定模式。

您还可以映射本机查询(即纯 SQL 查询)。为此,您需要使用 @SqlResultSetMapping(或者如果您要定义多个结果集映射,则使用 @SqlResultSetMappings)来描述 SQL 结果集结构。与 @NamedQuery 类似,@SqlResultSetMapping 可以在类级别或 JPA XML 文件中定义。但是它的范围对于应用程序是全局的。

正如我们将看到的,resultSetMapping 参数是在 @NamedNativeQuery 中定义的,它表示已定义的 @SqlResultSetMapping 的名称。结果集映射声明此本机查询检索的实体。实体的每个字段都绑定到一个 SQL 别名(或列名)。实体的所有字段,包括子类的字段和相关实体的外键列,都必须出现在 SQL 查询中。字段定义是可选的,前提是它们映射到类属性上声明的相同列名。

@NamedNativeQuery(name="night&area", query="select night.id nid, night.night_duration, "

    + " night.night_date, area.id aid, night.area_id, area.name "
    + "from Night night, Area area where night.area_id = area.id", 
                  resultSetMapping="joinMapping")
@SqlResultSetMapping(name="joinMapping", entities={
    @EntityResult(entityClass=Night.class, fields = {
        @FieldResult(name="id", column="nid"),
        @FieldResult(name="duration", column="night_duration"),
        @FieldResult(name="date", column="night_date"),
        @FieldResult(name="area", column="area_id"),
        discriminatorColumn="disc"
    }),
    @EntityResult(entityClass=org.hibernate.test.annotations.query.Area.class, fields = {
        @FieldResult(name="id", column="aid"),
        @FieldResult(name="name", column="name")
    })
    }
)

在上面的例子中,night&area 命名查询使用 joinMapping 结果集映射。此映射返回 2 个实体,NightArea,每个属性都已声明并与一个列名关联,实际上是查询检索到的列名。现在让我们看看属性/列的隐式声明。

@Entity

@SqlResultSetMapping(name="implicit",
                     entities=@EntityResult(entityClass=SpaceShip.class))
@NamedNativeQuery(name="implicitSample", 
                  query="select * from SpaceShip", 
                  resultSetMapping="implicit")
public class SpaceShip {
    private String name;
    private String model;
    private double speed;
    @Id
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Column(name="model_txt")
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }
    public double getSpeed() {
        return speed;
    }
    public void setSpeed(double speed) {
        this.speed = speed;
    }
}

在这个例子中,我们只描述了结果集映射的实体成员。属性/列映射使用实体映射值完成。在这种情况下,model 属性绑定到 model_txt 列。如果与相关实体的关联涉及复合主键,则应为每个外键列使用 @FieldResult 元素。 @FieldResult 名称由关系的属性名称组成,后跟一个点(“.”),然后是主键的字段或属性的名称。

@Entity

@SqlResultSetMapping(name="compositekey",
        entities=@EntityResult(entityClass=SpaceShip.class,
            fields = {
                    @FieldResult(name="name", column = "name"),
                    @FieldResult(name="model", column = "model"),
                    @FieldResult(name="speed", column = "speed"),
                    @FieldResult(name="captain.firstname", column = "firstn"),
                    @FieldResult(name="captain.lastname", column = "lastn"),
                    @FieldResult(name="dimensions.length", column = "length"),
                    @FieldResult(name="dimensions.width", column = "width")
                    }),
        columns = { @ColumnResult(name = "surface"),
                    @ColumnResult(name = "volume") } )
@NamedNativeQuery(name="compositekey",
    query="select name, model, speed, lname as lastn, fname as firstn, length, width, length * width as surface from SpaceShip", 
    resultSetMapping="compositekey")
} )
public class SpaceShip {
    private String name;
    private String model;
    private double speed;
    private Captain captain;
    private Dimensions dimensions;
    @Id
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumns( {
            @JoinColumn(name="fname", referencedColumnName = "firstname"),
            @JoinColumn(name="lname", referencedColumnName = "lastname")
            } )
    public Captain getCaptain() {
        return captain;
    }
    public void setCaptain(Captain captain) {
        this.captain = captain;
    }
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }
    public double getSpeed() {
        return speed;
    }
    public void setSpeed(double speed) {
        this.speed = speed;
    }
    public Dimensions getDimensions() {
        return dimensions;
    }
    public void setDimensions(Dimensions dimensions) {
        this.dimensions = dimensions;
    }
}
@Entity
@IdClass(Identity.class)
public class Captain implements Serializable {
    private String firstname;
    private String lastname;
    @Id
    public String getFirstname() {
        return firstname;
    }
    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }
    @Id
    public String getLastname() {
        return lastname;
    }
    public void setLastname(String lastname) {
        this.lastname = lastname;
    }
}

如果您检索单个实体,并且使用默认映射,则可以使用 resultClass 属性代替 resultSetMapping

@NamedNativeQuery(name="implicitSample", query="select * from SpaceShip",

    resultClass=SpaceShip.class)
public class SpaceShip {

在某些本机查询中,您需要返回标量值,例如在构建报表查询时。您可以通过 @ColumnResult@SqlResultsetMapping 中映射它们。实际上,您甚至可以在同一个本机查询中混合使用实体和标量返回值(尽管这可能不太常见)。

@SqlResultSetMapping(name="scalar", columns=@ColumnResult(name="dimension"))

@NamedNativeQuery(name="scalar", query="select length*width as dimension from SpaceShip", resultSetMapping="scalar")

另一个特定于本机查询的查询提示已引入:org.hibernate.callable,它可以是 true 或 false,具体取决于查询是否是存储过程。

Hibernate 3.1 提供了各种附加注解,您可以将这些注解与 EJB 3 实体混合和匹配使用。它们被设计为 EJB3 注解的自然扩展。

为了增强 EJB3 的功能,Hibernate 提供了与 Hibernate 特性匹配的特定注解。 org.hibernate.annotations 包包含所有这些注解扩展。

您可以微调 Hibernate 在实体上执行的一些操作,这些操作超出了 EJB3 规范提供的范围。

@org.hibernate.annotations.Entity 添加了可能需要超出标准 @Entity 中定义的内容的额外元数据

以下是 Hibernate 注解扩展的一些额外内容

@org.hibernate.annotations.BatchSize 允许您定义获取此实体实例时的批次大小(例如 @BatchSize(size=4))。当加载给定实体时,Hibernate 将加载持久化上下文中的所有相同类型未初始化的实体,最多到批次大小。

@org.hibernate.annotations.Proxy 定义实体的延迟加载属性。lazy(默认为 true)定义类是否延迟加载。proxyClassName 是用于生成代理的接口(默认为类本身)。

@org.hibernate.annotations.Where 定义在检索此类的实例时使用的可选 SQL WHERE 子句。

@org.hibernate.annotations.Check 定义在 DDL 语句中定义的可选检查约束。

@OnDelete(action=OnDeleteAction.CASCADE) 在连接的子类上:在删除时使用 SQL 级联删除,而不是常规的 Hibernate 机制。

@Table(appliesTo="tableName", indexes = { @Index(name="index1", columnNames={"column1", "column2"} ) } ) 在表 tableName 的列上创建定义的索引。这可以应用于主表或任何辅助表。 @Tables 注解允许您对不同的表应用索引。此注解预期在出现 @javax.persistence.Table@javax.persistence.SecondaryTable(s) 的地方。

@org.hibernate.annotations.Table 也可以用来定义二级表的以下元素

@Immutable 将实体或集合标记为不可变。应用程序不能更新不可变实体。这允许 Hibernate 进行一些较小的性能优化。对不可变实体的更新将被忽略,但不会引发异常。 @Immutable 必须仅用于根实体。放置在集合上的 @Immutable 使集合不可变,这意味着不允许对集合进行添加和删除操作。在这种情况下,将抛出 HibernateException

@Persister 允许您定义自己的自定义持久化策略。例如,您可以指定 org.hibernate.persister.EntityPersister 的自己的子类,或者甚至提供 org.hibernate.persister.ClassPersister 接口的全新实现,该接口通过例如存储过程调用、序列化到平面文件或 LDAP 来实现持久化。

@Entity

@BatchSize(size=5)
@org.hibernate.annotations.Entity(
        selectBeforeUpdate = true,
        dynamicInsert = true, dynamicUpdate = true,
        optimisticLock = OptimisticLockType.ALL,
        polymorphism = PolymorphismType.EXPLICIT)
@Where(clause="1=1")
@org.hibernate.annotations.Table(name="Forest", indexes = { @Index(name="idx", columnNames = { "name", "length" } ) } )
@Persister(impl=MyEntityPersister.class)
public class Forest { ... }
@Entity

@Inheritance(
    strategy=InheritanceType.JOINED
)
public class Vegetable { ... }
@Entity
@OnDelete(action=OnDeleteAction.CASCADE)
public class Carrot extends Vegetable { ... }

Hibernate 注释在定义标识符时超越了 Java 持久性规范。

@org.hibernate.annotations.Type 覆盖了使用的默认 hibernate 类型:这通常不是必需的,因为 hibernate 会正确推断出类型。有关 Hibernate 类型的更多信息,请参阅 Hibernate 参考指南。

@org.hibernate.annotations.TypeDef@org.hibernate.annotations.TypeDefs 允许您声明类型定义。这些注释可以放在类或包级别。请注意,这些定义对于会话工厂是全局的(即使是在类级别定义的)。如果类型用于单个实体,则可以将定义放在实体本身。否则,建议将定义放在包级别。在下面的示例中,当 Hibernate 遇到 PhoneNumer 类的属性时,它将持久化策略委托给自定义映射类型 PhoneNumberType。但是,属于其他类的属性也可以通过显式使用 @Type 注释将它们的持久化策略委托给 PhoneNumberType

@TypeDef(

   name = "phoneNumber",
   defaultForType = PhoneNumber.class,
   typeClass = PhoneNumberType.class
)
@Entity
public class ContactDetails {
   [...]
   private PhoneNumber localPhoneNumber;
   @Type(type="phoneNumber")
   private OverseasPhoneNumber overseasPhoneNumber;
   [...]
}

以下示例展示了 parameters 属性在自定义 TypeDef 中的使用。

//in org/hibernate/test/annotations/entity/package-info.java

@TypeDefs(
    {
    @TypeDef(
        name="caster",
        typeClass = CasterStringType.class,
        parameters = {
            @Parameter(name="cast", value="lower")
        }
    )
    }
)
package org.hibernate.test.annotations.entity;
//in org/hibernate/test/annotations/entity/Forest.java
public class Forest {
    @Type(type="caster")
    public String getSmallText() {
    ...
}      

当使用复合用户类型时,您需要表达列定义。为此目的,引入了 @Columns

@Type(type="org.hibernate.test.annotations.entity.MonetaryAmountUserType")

@Columns(columns = {
    @Column(name="r_amount"),
    @Column(name="r_currency")
})
public MonetaryAmount getAmount() {
    return amount;
}
public class MonetaryAmount implements Serializable {
    private BigDecimal amount;
    private Currency currency;
    ...
}

默认情况下,当 Hibernate 无法解析关联(因为预期的关联元素不在数据库中(关联列上的 id 错误)时,Hibernate 会引发异常。对于遗留和维护不善的模式,这可能很不方便。您可以要求 Hibernate 忽略这些元素,而不是使用 @NotFound 注释引发异常。此注释可用于 @OneToOne(带 FK)、@ManyToOne@OneToMany@ManyToMany 关联。

@Entity

public class Child {
    ...
    @ManyToOne
    @NotFound(action=NotFoundAction.IGNORE)
    public Parent getParent() { ... }
    ...
}

有时您希望将级联删除委托给您的数据库。当给定实体被删除时。

@Entity

public class Child {
    ...
    @ManyToOne
    @OnDelete(action=OnDeleteAction.CASCADE)
    public Parent getParent() { ... }
    ...
}

在这种情况下,Hibernate 在数据库级别生成级联删除约束。

外部键约束虽然是由 Hibernate 生成的,但名称相当难以理解。您可以使用 @ForeignKey 覆盖约束名称。

@Entity

public class Child {
    ...
    @ManyToOne
    @ForeignKey(name="FK_PARENT")
    public Parent getParent() { ... }
    ...
}
alter table Child add constraint FK_PARENT foreign key (parent_id) references Parent

JPA 带有 fetch 选项来定义延迟加载和获取模式,但 Hibernate 在这个领域有更多的选项集。为了微调延迟加载和获取策略,引入了一些额外的注释

Hibernate 注释覆盖了 EJB3 获取选项。


注解 @Any 定义了来自多个表的类的多态关联。这种类型的映射始终需要多个列。第一列保存关联实体的类型。其余列保存标识符。不可能为这种类型的关联指定外键约束,因此它肯定不是映射(多态)关联的常用方式。你应该只在非常特殊的情况下使用它(例如审计日志、用户会话数据等)。

注解 @Any 描述了保存元数据信息的列。为了将元数据信息的价值与实际实体类型联系起来,使用了注解 @AnyDef@AnyDefs

    @Any( metaColumn = @Column( name = "property_type" ), fetch=FetchType.EAGER )

    @AnyMetaDef( 
        idType = "integer", 
        metaType = "string", 
        metaValues = {
            @MetaValue( value = "S", targetEntity = StringProperty.class ),
            @MetaValue( value = "I", targetEntity = IntegerProperty.class )
        } )
    @JoinColumn( name = "property_id" )
    public Property getMainProperty() {
        return mainProperty;
    }

idType 表示目标实体的标识符属性类型,而 metaType 表示元数据类型(通常为 String)。

请注意,@AnyDef 可以互用和重复使用。建议在这种情况下将其作为包元数据放置。

//on a package

@AnyMetaDef( name="property" 
    idType = "integer", 
    metaType = "string", 
    metaValues = {
        @MetaValue( value = "S", targetEntity = StringProperty.class ),
        @MetaValue( value = "I", targetEntity = IntegerProperty.class )
    } )
package org.hibernate.test.annotations.any;
//in a class
    @Any( metaDef="property", metaColumn = @Column( name = "property_type" ), fetch=FetchType.EAGER )
    @JoinColumn( name = "property_id" )
    public Property getMainProperty() {
        return mainProperty;
    }

可以使用以下方法设置

您还可以声明一个排序比较器。使用 @Sort 注解。表达您想要的比较器类型,可以在无序、自然或自定义比较器之间进行选择。如果您想使用自己的比较器实现,则还需要使用 comparator 属性表达实现类。请注意,您需要使用 SortedSetSortedMap 接口之一。

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)

    @JoinColumn(name="CUST_ID")
    @Sort(type = SortType.COMPARATOR, comparator = TicketComparator.class)
    @Where(clause="1=1")
    @OnDelete(action=OnDeleteAction.CASCADE)
    public SortedSet<Ticket> getTickets() {
        return tickets;
    }

有关这些注解的更多信息,请参阅之前的描述。

虽然外键约束由 Hibernate 生成,但它们的名字相当难以理解。您可以通过使用 @ForeignKey 覆盖约束名称。请注意,此注解必须放置在关系的拥有端,inverseName 引用另一端的约束。

@Entity

public class Woman {
    ...
    @ManyToMany(cascade = {CascadeType.ALL})
    @ForeignKey(name = "TO_WOMAN_FK", inverseName = "TO_MAN_FK")
    public Set<Man> getMens() {
        return mens;
    }
}
alter table Man_Woman add constraint TO_WOMAN_FK foreign key (woman_id) references Woman
alter table Man_Woman add constraint TO_MAN_FK foreign key (man_id) references Man

Hibernate 能够在您的数据之上应用任意过滤器。这些过滤器在运行时应用于给定的会话。首先,您需要定义它们。

@org.hibernate.annotations.FilterDef@FilterDefs 定义了过滤器定义,这些定义由使用相同名称的过滤器使用。过滤器定义具有 name() 和参数数组。参数允许您在运行时调整过滤器的行为。每个参数由一个 @ParamDef 定义,该定义具有名称和类型。您还可以为给定的 @FilterDef 定义一个 defaultCondition() 参数,以设置在每个单独的 @Filter 中未定义任何条件时要使用的默认条件。可以在类级或包级定义 @FilterDef

现在,我们需要定义应用于实体加载或集合加载的 SQL 过滤器子句。@Filter 用于放置在实体或集合元素上

@Entity

@FilterDef(name="minLength", parameters=@ParamDef( name="minLength", type="integer" ) )
@Filters( {
    @Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"),
    @Filter(name="minLength", condition=":minLength <= length")
} )
public class Forest { ... }

当集合使用关联表作为关系表示时,您可能希望将过滤器条件应用于关联表本身或目标实体表。要将约束应用于目标实体,请使用常规的 @Filter 注解。但是,如果您希望定位关联表,请使用 @FilterJoinTable 注解。

    @OneToMany

    @JoinTable
    //filter on the target entity table
    @Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length")
    //filter on the association table
    @FilterJoinTable(name="security", condition=":userlevel >= requredLevel")
    public Set<Forest> getForests() { ... }

Hibernate 使您能够覆盖生成的每个 SQL 语句。我们已经看到了本机 SQL 查询的使用,但您也可以覆盖用于加载或更改实体状态的 SQL 语句。

@Entity

@Table(name="CHAOS")
@SQLInsert( sql="INSERT INTO CHAOS(size, name, nickname, id) VALUES(?,upper(?),?,?)")
@SQLUpdate( sql="UPDATE CHAOS SET size = ?, name = upper(?), nickname = ? WHERE id = ?")
@SQLDelete( sql="DELETE CHAOS WHERE id = ?")
@SQLDeleteAll( sql="DELETE CHAOS")
@Loader(namedQuery = "chaos")
@NamedNativeQuery(name="chaos", query="select id, size, name, lower( nickname ) as nickname from CHAOS where id= ?", resultClass = Chaos.class)
public class Chaos {
    @Id
    private Long id;
    private Long size;
    private String name;
    private String nickname;

@SQLInsert@SQLUpdate@SQLDelete@SQLDeleteAll 分别覆盖 INSERT 语句、UPDATE 语句、DELETE 语句、DELETE 语句以删除所有实体。

如果您希望调用存储过程,请确保将 callable 属性设置为 true (@SQLInsert(callable=true, ...))。

为了检查执行是否正确,Hibernate 允许您定义以下三种策略之一

要定义结果检查样式,请使用 check 参数 (@SQLUpdate(check=ResultCheckStyle.COUNT, ...))。

您还可以通过本机 SQL 查询或 HQL 查询覆盖 SQL 加载语句。您只需使用 @Loader 注解引用一个命名查询。

您可以使用完全相同的注解集来覆盖与集合相关的语句。

@OneToMany

@JoinColumn(name="chaos_fk")
@SQLInsert( sql="UPDATE CASIMIR_PARTICULE SET chaos_fk = ? where id = ?")
@SQLDelete( sql="UPDATE CASIMIR_PARTICULE SET chaos_fk = null where id = ?")
private Set<CasimirParticle> particles = new HashSet<CasimirParticle>();

参数的顺序很重要,由 Hibernate 处理属性的顺序定义。您可以通过为 org.hibernate.persister.entity 级别启用调试日志来查看预期的顺序。启用此级别后,Hibernate 将打印出用于创建、更新、删除等实体的静态 SQL。(要查看预期的顺序,请记住不要通过注解包含自定义 SQL,因为这将覆盖 Hibernate 生成的静态 sql。)

使用 @org.hibernate.annotations.Table 和属性 sqlInsertsqlUpdatesqlDelete 之一(或全部)也可以覆盖辅助表的 SQL 语句

@Entity

@SecondaryTables({
    @SecondaryTable(name = "`Cat nbr1`"),
    @SecondaryTable(name = "Cat2"})
@org.hibernate.annotations.Tables( {
    @Table(appliesTo = "Cat", comment = "My cat table" ),
    @Table(appliesTo = "Cat2", foreignKey = @ForeignKey(name="FK_CAT2_CAT"), fetch = FetchMode.SELECT,
        sqlInsert=@SQLInsert(sql="insert into Cat2(storyPart2, id) values(upper(?), ?)") )
} )
public class Cat implements Serializable {

前面的示例还表明,您可以为给定的表(主表或辅助表)提供注释:此注释将用于 DDL 生成。

第 2.4.5.1 节,“延迟选项和获取模式” 中,我们看到了如何使用 @Fetch 注解影响关联对象的获取策略。 另一种方法是所谓的获取配置文件。 获取配置文件是与 org.hibernate.SessionFactory 关联的命名配置,它将在 org.hibernate.Session. 上启用。 一旦在 org.hibernate.Session 上启用,获取配置文件将对该会话生效,直到明确禁用它。 让我们看一个例子

@Entity

@FetchProfile(name = "customer-with-orders", fetchOverrides = {
   @FetchProfile.FetchOverride(entity = Customer.class, association = "orders", mode = FetchMode.JOIN)
})
public class Customer {
   @Id
   @GeneratedValue
   private long id;
   private String name;
   private long customerNumber;
   @OneToMany
   private Set<Order> orders;
   // standard getter/setter
   ...
}

在正常情况下,orders 关联将由 Hibernate 延迟加载,但在将客户及其订单一起加载效率更高的用例中,您可以执行以下操作

Session session = ...;

session.enableFetchProfile( "customer-with-orders" );  // name matches @FetchProfile name
Customer customer = (Customer) session.get( Customer.class, customerId );
session.disableFetchProfile( "customer-with-orders" ); // or just close the session
...

注意

获取配置文件定义是全局的,它与您将它们放在哪个类上无关。 您可以将 @FetchProfile 注解放在类或包(package-info.java)上。 为了定义同一类或包的多个获取配置文件,可以使用 @FetchProfiles

目前只支持连接风格的获取配置文件,但计划支持其他风格。 有关详细信息,请参见 HHH-3414。 另请参阅 Hibernate Core 文档中关于获取配置文件的讨论。

EJB3 中元数据的主要目标是注解,但 EJB3 规范提供了一种通过 XML 部署描述符覆盖或替换注解定义的元数据的方法。 在当前版本中,只支持纯 EJB3 注解覆盖。 如果您希望在某些实体中使用 Hibernate 特定功能,您必须使用注解或回退到 hbm 文件。 当然,您可以混合使用注解实体和 hbm 文件中描述的实体。

单元测试套件显示了一些额外的 XML 文件示例。

XML 部署描述符结构旨在反映注解结构。 因此,如果您知道注解结构,那么使用 XML 架构将非常简单。

您可以定义一个或多个描述元数据的 XML 文件,这些文件将被覆盖引擎合并。

您可以定义或覆盖给定实体上的元数据信息。

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappin(1)gs 
  xmlns="http://java.sun.com/xml/ns/persistence/orm"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd"
  version="2.0">

    <package>o(2)rg.hibernate.test.annotations.reflection</package>
    <entity cl(3)ass="Administration" access="PROPERTY" metadata-complete="true">
        <table(4) name="tbl_admin">
            <unique-constraint>
                <column-name>firstname</column-name>
                <column-name>lastname</column-name>
            </unique-constraint>
        </table>
        <secon(5)dary-table name="admin2">
            <primary-key-join-column name="admin_id" referenced-column-name="id"/>
            <unique-constraint>
                <column-name>address</column-name>
            </unique-constraint>
        </secondary-table>
        <id-cl(6)ass class="SocialSecurityNumber"/>
        <inher(7)itance strategy="JOINED"/>
        <seque(8)nce-generator name="seqhilo" sequence-name="seqhilo"/>
        <table(9)-generator name="table" table="tablehilo"/>
        ...
    </entity>

    <entity class="PostalAdministration">
        <prima(10)ry-key-join-column name="id"/>
        ...
    </entity>
</entity-mappings>

1

entity-mappings: entity-mappings 是所有 XML 文件的根元素。 您必须声明 xml 架构,架构文件包含在 hibernate-annotations.jar 文件中,Hibernate 注解不会处理任何互联网访问。

2

package (可选): 用于给定部署描述符文件中所有未限定类名的默认包。

3

entity: 描述一个实体。

metadata-complete 定义此元素的元数据描述是否完整(换句话说,是否应考虑类级别上的注解)。

实体必须有一个 class 属性,引用元数据适用的 java 类。

您可以通过 name 属性覆盖实体名称,如果没有定义,并且如果存在 @Entity.name,则使用它(前提是元数据完整没有设置)。

对于元数据完整(见下文)元素,您可以定义一个 accessFIELDPROPERTY(默认值))。 对于非元数据完整元素,如果未定义 access,则 @Id 位置将导致位置,如果定义了 access,则使用该值。

4

table: 您可以声明表属性(名称、架构、目录),如果没有定义,则使用 java 注解。

您可以定义一个或多个唯一约束,如示例所示

5

secondary-table: 定义辅助表,非常类似于常规表,只是您可以通过 primary-key-join-column 元素定义主键/外键列。 在非元数据完整上,只有在没有 secondary-table 定义的情况下才会使用注解辅助表,否则注解将被忽略。

6

id-class: 以类似于 @IdClass 的方式定义 id 类

7

inheritance: 定义继承策略(JOINEDTABLE_PER_CLASSSINGLE_TABLE),仅在根实体级别可用

8

sequence-generator: 定义一个序列生成器

9

table-generator: 定义一个表生成器

10

primary-key-join-column: 定义子实体使用 JOINED 继承策略时的主键连接列

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings 
  xmlns="http://java.sun.com/xml/ns/persistence/orm"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd"
  version="2.0">

    <package>org.hibernate.test.annotations.reflection</package>
    <entity class="Music" access="PROPERTY" metadata-complete="true">
        <discr(1)iminator-value>Generic</discriminator-value>
        <discriminator-column length="34"/>
        ...
    </entity>

    <entity class="PostalAdministration">
        <primary-key-join-column name="id"/>
        <named(2)-query name="adminById">
            <query>select m from Administration m where m.id = :id</query>
            <hint name="org.hibernate.timeout" value="200"/>
        </named-query>
        <named(3)-native-query name="allAdmin" result-set-mapping="adminrs">
            <query>select *, count(taxpayer_id) as taxPayerNumber 
            from Administration, TaxPayer
            where taxpayer_admin_id = admin_id group by ...</query>
            <hint name="org.hibernate.timeout" value="200"/>
        </named-native-query>
        <sql-r(4)esult-set-mapping name="adminrs">
            <entity-result entity-class="Administration">
                <field-result name="name" column="fld_name"/>
            </entity-result>
            <column-result name="taxPayerNumber"/>
        </sql-result-set-mapping>
        <attri(5)bute-override name="ground">
            <column name="fld_ground" unique="true" scale="2"/>
        </attribute-override>
        <association-override name="referer">
            <join-column name="referer_id" referenced-column-name="id"/>
        </association-override>
        ...
    </entity>
</entity-mappings>

1

discriminator-value / discriminator-column: 定义鉴别器值和保存它的列,当选择 SINGLE_TABLE 继承策略时

2

named-query: 定义命名查询,并可能定义与其关联的提示。 这些定义是对注解中定义的定义的补充,如果两个定义具有相同的名称,则 XML 定义优先。

3

named-native-query: 定义命名本机查询及其 SQL 结果集映射。 或者,您可以定义 result-class。 这些定义是对注解中定义的定义的补充,如果两个定义具有相同的名称,则 XML 定义优先。

4

sql-result-set-mapping: 描述结果集映射结构。 您可以定义实体和列映射。 这些定义是对注解中定义的定义的补充,如果两个定义具有相同的名称,则 XML 定义优先

5

attribute-override / association-override: 定义列或连接列覆盖。 此覆盖是对注解中定义的覆盖的补充

同样适用于 <embeddable><mapped-superclass>

当然,您可以为属性定义 XML 覆盖。 如果定义了元数据完整,则将忽略其他属性(即在 Java 级别)。 否则,一旦您开始覆盖属性,就会忽略给定属性上的所有注解。 所有属性级别元数据都在 entity/attributesmapped-superclass/attributesembeddable/attributes 中表现出来。


    <attributes>
        <id name="id">
            <column name="fld_id"/>
            <generated-value generator="generator" strategy="SEQUENCE"/>
            <temporal>DATE</temporal>
            <sequence-generator name="generator" sequence-name="seq"/>
        </id>
        <version name="version"/>
        <embedded name="embeddedObject">
            <attribute-override name"subproperty">
                <column name="my_column"/>
            </attribute-override>
        </embedded>
        <basic name="status" optional="false">
            <enumerated>STRING</enumerated>
        </basic>
        <basic name="serial" optional="true">
            <column name="serialbytes"/>
            <lob/>
        </basic>
        <basic name="terminusTime" fetch="LAZY">
            <temporal>TIMESTAMP</temporal>
        </basic>
    </attributes>

您可以通过 idembedded-idversionembeddedbasic 覆盖属性。 这些元素中的每一个都可以有相应的子元素:lobtemporalenumeratedcolumn

您可以为关联定义 XML 覆盖。 所有关联级别元数据都在 entity/attributesmapped-superclass/attributesembeddable/attributes 中表现出来。


    <attributes>
        <one-to-many name="players" fetch="EAGER">
            <map-key name="name"/>
            <join-column name="driver"/>
            <join-column name="number"/>
        </one-to-many>
        <many-to-many name="roads" target-entity="Administration">
            <order-by>maxSpeed</order-by>
            <join-table name="bus_road">
                <join-column name="driver"/>
                <join-column name="number"/>
                <inverse-join-column name="road_id"/>
                <unique-constraint>
                    <column-name>driver</column-name>
                    <column-name>number</column-name>
                </unique-constraint>
            </join-table>
        </many-to-many>
        <many-to-many name="allTimeDrivers" mapped-by="drivenBuses">
    </attributes>

您可以通过 one-to-manyone-to-onemany-to-onemany-to-many 覆盖关联。 这些元素中的每一个都可以有相应的子元素:join-table(它可以具有 join-columninverse-join-column)、join-columnsmap-keyorder-bymapped-bytarget-entity 可以在有意义的情况下定义为属性。 结构再次反映了注解结构。 您可以在描述注解的章节中找到所有语义信息。

Hibernate 注解主要关注持久化元数据。 该项目还与一些外部模块有很好的集成。

Bean 验证标准化了如何定义和声明域模型级约束。 例如,您可以表达某个属性永远不应该为 null,账户余额应该严格为正数等。 这些域模型约束通过对 bean 的属性进行注解,在 bean 本身中声明。 然后,Bean 验证可以读取它们并检查约束违规。 验证机制可以在应用程序的不同层中执行,而无需复制任何这些规则(表示层、数据访问层)。 遵循 DRY 原则,Bean 验证及其参考实现 Hibernate Validator 就是为此目的而设计的。

Hibernate 和 Bean 验证之间的集成在两个级别上运作。 首先,它能够检查类的内存实例是否存在约束违规。 其次,它可以将约束应用于 Hibernate 元模型,并将它们合并到生成的数据库模式中。

每个约束注解都与一个验证器实现相关联,该验证器实现负责检查实体实例上的约束。 验证器还可以(可选)将约束应用于 Hibernate 元模型,从而允许 Hibernate 生成表达约束的 DDL。 使用适当的事件监听器,您可以在 Hibernate 执行的插入、更新和删除操作上执行检查操作。

在运行时检查实例时,Hibernate Validator 在一组 ConstraintViolation 中返回有关约束违规的信息。 除了其他信息外,ConstraintViolation 还包含一个错误描述消息,该消息可以嵌入与注解捆绑的参数值(例如,大小限制)和可以外部化为 ResourceBundle 的消息字符串。

默认情况下,不需要任何配置。

Default 组在实体插入和更新时进行验证,并且数据库模型也根据 Default 组进行相应更新。

您可以通过设置验证模式来自定义 Bean 验证集成。 使用 javax.persistence.validation.mode 属性,并在您的 persistence.xml 文件或 hibernate.cfg.xml 文件中对其进行设置。 有多种选择

如果要分别在插入、更新和删除期间验证不同的组,请使用

每个属性都接受用逗号 (,) 分隔的验证组的完全限定类名


注意

您可以在 hibernate.cfg.xmlhibernate.properties 或以编程方式设置这些属性。

注释是一种非常方便且优雅的方式来指定域模型的不可变约束。例如,您可以表达属性永远不应该为空,帐户余额应该严格为正等等。这些域模型约束通过注释其属性在 bean 本身中声明。然后,验证器可以读取它们并检查约束违规情况。验证机制可以在应用程序的不同层执行,而无需重复任何这些规则(表示层、数据访问层)。遵循 DRY 原则,Hibernate Validator 就是为此目的而设计的。

Hibernate Validator 可以在两个级别工作。首先,它能够检查类的内存实例是否有约束违规。其次,它可以将约束应用于 Hibernate 元模型并将它们合并到生成的数据库模式中。

每个约束注释都与一个验证器实现相关联,该实现负责检查实体实例上的约束。验证器也可以(可选地)将约束应用于 Hibernate 元模型,允许 Hibernate 生成表达约束的 DDL。使用适当的事件监听器,您可以在 Hibernate 执行的插入和更新操作上执行检查操作。Hibernate Validator 不限于与 Hibernate 一起使用。您可以在应用程序中的任何地方轻松使用它。

在运行时检查实例时,Hibernate Validator 返回有关约束违规的信息,这些信息存储在 InvalidValue 的数组中。除其他信息外,InvalidValue 包含错误描述消息,该消息可以嵌入带有注释的参数值捆绑(例如,长度限制),以及可以外部化为 ResourceBundle 的消息字符串。