添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Hibernate Annotations


目录

前言
1. 翻译说明
2. 版权声明
前言
1. 创建一个注解项目
1.1. 系统需求
1.2. 系统配置
2. 实体Bean
2.1. 简介
2.2. 用EJB3注解进行映射
2.2.1. 声明实体bean
2.2.1.1. 定义表(Table)
2.2.1.2. 乐观锁定版本控制
2.2.2. 映射简单属性
2.2.2.1. 声明基本的属性映射
2.2.2.2. 声明列属性
2.2.2.3. 嵌入式对象(又名组件)
2.2.2.4. 无注解之属性的默认值
2.2.. 映射主键属性
2.2.4. 映射继承关系
2.2.4.1. 每个类一张表
2.2.4.2. 每个类层次结构一张表
2.2.4.3. 连接的子类
2.2.4.4. 从父类继承的属性
2.2.5. 映射实体Bean的关联关系
2.2.5.1. 一对一(One-to-one)
2.2.5.2. 多对一(Many-to-one)
2.2.5.3. 集合类型
2.2.5.4. 用cascading实现传播性持久化(Transitive persistence)
2.2.5.5. 关联关系获取
2.2.6. 映射复合主键与外键
2.2.7. 映射二级表(secondary tables)
2.3. 映射查询
2.3.1. 映射EJBQL/HQL查询
2.3.2. 映射本地化查询
2.4. Hibernate独有的注解扩展
2.4.1. 实体
2.4.2. 标识符
2.4.3. 属性
2.4.3.1. 访问类型
2.4.3.2. 公式
2.4.3.3. 类型
2.4.3.4. 索引
2.4.3.5. @Parent
2.4.3.6. 生成的属性
2.4.4. 继承
2.4.5. 关于单个关联关系的注解
2.4.5.1. 延迟选项和获取模式
2.4.6. 关于集合类型的注解
2.4.6.1. 参数注解
2.4.6.2. 更多的集合类型
2.4.7. 缓存
2.4.8. 过滤器
2.4.9. 查询
3. 通过XML覆写元数据
3.1. 原则
3.1.1. 全局级别的元数据
3.1.2. 实体级别的元数据
3.1.3. 属性级别的元数据
3.1.4. 关联级别的元数据
4. Hibernate验证器
4.1. 约束
4.1.1. 什么是约束?
4.1.2. 内建约束
4.1.3. 错误信息
4.1.4. 编写你自己的约束
4.1.5. 注解你的领域模型
4.2. 使用验证器框架
4.2.1. 数据库schema层次验证
4.2.2. Hibernate基于事件的验证
4.2.3. 程序级验证
4.2.4. 验证信息
5. Hibernate与Lucene集成
5.1. 使用Lucene为实体建立索引
5.1.1. 注解领域模型
5.1.2. 启用自动索引
A. 术语表

WARNING! This is a translated version of the English Hibernate reference documentation. The translated version might not be up to date! However, the differences should only be very minor. Consult the English reference documentation if you are missing information or encounter a translation error. If you like to contribute to a particular translation, contact us on the Hibernate developer mailing list.

Translator(s): RedSaga Translate Team 满江红翻译团队 <[email protected]>

本文档的翻译是在网络上协作进行的,也会不断根据Hibernate的升级进行更新。提供此文档的目的是为了减缓学习Hibernate的坡度,而非代替原文档。我们建议所有有能力的读者都直接阅读英文原文。若您对翻译有异议,或发现翻译错误,敬请不吝赐教,报告到如下地址:http://wiki.redsaga.com/confluence/display/HART/Home

关于我们

正如其他的ORM工具,Hibernate同样需要元数据来控制在不同数据表达形式之间的转化. 在Hibernate 2.x里,多数情况下表示映射关系的元数据保存在XML文本文件中. 还有一种方式就是Xdoclet,它可以在编译时利用Javadoc中的源码注释信息来进行预处理. 现在新的JDK标准(JDK1.5以上)也支持类似的注解功能,但相比之下很多工具对此提供了更强大更好用的支持. 以IntelliJ IDEA和Eclipse为例,这些IDE工具为JDK 5.0注解功能提供了自动完成和语法高亮功能. 注解被直接编译到字节码里,并 在运行时(对于Hibernate来讲就是启动的时候)通过反射读取这些注解, 因此外部XML文件就不再需要了.

EJB3规范最终认可了透明化ORM的成功范例以及市场对于这种技术的兴趣. EJB3规范标准化了ORM的基础API而且在任何ORM持久化机制中使用元数据. Hibernate EntityManager 实现了EJB3持久化规范中定义的编程接口和生命周期规则. 在 Hibernate Core 的基础上再结合 Hibernate Annotations 就实现了一套完整(并且独立)的EJB3持久化解决方案. 你可以结合三者来使用,也可以抛开EJB3编程接口和生命周期规则而独立使用注解, 甚至只单独使用 Hibernate Core . 这些都取决于项目的商业和技术上的实际需求. Hibernate允许你直接使用native APIs,如果有需要, 甚至可以直接操作JDBC和SQL.

注意本文档基于Hibernate Annotations的预览版(遵从EJB 3.0/JSR-220最终草案). 这个版本和新规范中定义的最终概念已经非常接近了.我们的目标是提供一套完整的ORM注解, 包括EJB3的标准注解以及Hibernate3的扩展(后者是EJB3规范中没有涉及到的). 最终通过注解你可以完成任何可能的映射.详情参考???.

EJB3最终草案修改了部分注解, http://www.hibernate.org/371.html提供了从上一个版本到最新版本的迁移指南.

首先就是设置classpath(当然是在IDE中创建了一个新项目之后)。

我们推荐在一个包装器(wrapper)类 HibernateUtil 的静态初始化代码块中启动Hibernate。或许你在Hibernate文档的其他很多地方看到过这个类, 但是要在你的项目中使用注解,还需要对这个辅助(helper)类进行扩展。扩展如下:

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().buildSessionFactory();
        } catch (Throwable ex) {
            // Log exception!
            throw new ExceptionInInitializerError(ex);
    public static Session getSession()
            throws HibernateException {
        return sessionFactory.openSession();
            

这里比较有意思的是使用到了 AnnotationConfiguration 类。 在XML配置文件(通常是 hibernate.cfg.xml )中则定义了包和经过注解的类。下面的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和注解。资源元素(resource element)可以是hbm文件也可以是EJB3 XML发布描述符,此差别对于配置过程是透明的。

除了上面的方式,你还可以通过编程的方式定义包括注解的类和包

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

你也可以使用Hibernate Entity Manager来完成以上功能。Hibernate Entity Manager有自己的一套配置机制,详情请参考相关文档。

除了启动方式和配置文件有所改变之外,结合注解来使用Hibernate API和以前没有什么区别, 在其他方面你还是可以继续保持以前的习惯和喜好( hibernate.properties hibernate.cfg.xml , programmatic APIs等等)。 甚至对于同一个 SessionFactory ,你都可以混合带注解的持久类以及传统的bm.cfg.xml声明方式。 然而你不能多次声明同一个类(要么通过注解要么通过hbm.xml配置文件), 而且在一个映射实体的类继承层次中,这两个配置策略不能同时使用.

为了简化从hbm文件到注解的迁移过程, 配置机制将自动检测在注解和hbm文件中重复的映射。 默认情况下hbm文件中的声明比类中的注解元数据具有更高的优先级。 这种优先级的设定是以类为单位的。 你也可以通过 hibernate.mapping.precedence 修改这种优先级。 默认的值是 hbm, class , 如果改为 class,hbm ,当发生冲突的时候,类中的注解将比hbm文件具有更高的优先级。

现在EJB3实体Bean是纯粹的POJO.实际上这表达了和Hibernate持久化实体对象同样的概念. 它们的映射都通过JDK5.0注解来定义(EJB3规范中的XML描述语法至今还没有最终定下来). 注解分为两个部分,分别是逻辑映射注解和物理映射注解, 通过逻辑映射注解可以描述对象模型,类之间的关系等等, 而物理映射注解则描述了物理的schema,表,列,索引等等. 下面我们在代码中将混合使用这两种类型的注解.

EJB3注解的API定义在 javax.persistence.* 包里面. 大部分和JDK5兼容的IDE(象Eclipse, IntelliJ IDEA 和Netbeans等等)都提供了注解接口和属性的自动完成功能. (这些不需要IDE提供特别的EJB3支持模块,因为EJB3注解是标准的JDK5注解)

请阅读JBoss EJB 3.0指南或者直接阅读Hibernate Annotations测试代码以获取更多的可运行实例.Hibernate Annotations提供的大部分单元测试代码都演示了实际的例子,是一个获取灵感的好地方.

每一个持久化POJO类都是一个实体bean,这可以通过在类的定义中使用 @Entity 注解来进行声明:

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

通过 @Entity 注解将一个类声明为一个实体bean(即一个持久化POJO类), @Id 注解则声明了该实体bean的标识属性. 其他的映射定义是隐式的.这种以隐式映射为主体,以显式映射为例外的配置方式在新的EJ3规范中处于非常重要的位置, 和以前的版本相比有了质的飞跃. 在上面这段代码中:Flight类映射到Flight表,并使用id列作为主键列.

在对一个类进行注解时,你可以选择对它的的属性或者方法进行注解,根据你的选择,Hibernate的访问类型分别为 field property . EJ3规范要求在需要访问的元素上进行注解声明,例如,如果访问类型为 property 就要在getter方法上进行注解声明, 如果访问类型为 field 就要在字段上进行注解声明.应该尽量避免混合使用这两种访问类型. Hibernate根据 @Id @EmbeddedId 的位置来判断访问类型.

Every non static non transient property (field or method) of an entity bean is considered persistent, unless you annotate it as @Transient . Not having an annotation for your property is equivalent to the appropriate @Basic annotation. The @Basic annotation allows you to declare the fetching strategy for a property:

实体bean中所有的非static非transient的属性都可以被持久化, 除非你将其注解为 @Transient .所有没有定义注解的属性等价于在其上面添加了@Basic注解. 通过 @Basic 注解可以声明属性的获取策略(fetch strategy):

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 是一个transient的字段, lengthInMeter 的getter方法被注解为 @Transient , entity manager将忽略这些字段和属性. 而 name , length , firstname 这几个属性则是被定义为可持久化和可获取的.对于简单属性来说,默认的获取方式是即时获取(early fetch). 当一个实体Bean的实例被创建时,Hibernate会将这些属性的值从数据库中提取出来,保存到Bean的属性里. 与即时获取相对应的是延迟获取(lazy fetch).如果一个属性的获取方式是延迟获取 (比如上面例子中的 detailedComment 属性), Hibernate在创建一个实体Bean的实例时,不会即时将这个属性的值从数据库中读出. 只有在该实体Bean的这个属性第一次被调用时,Hibernate才会去获取对应的值. 通常你不需要对简单属性设置延迟获取(lazy simple property),千万不要和延迟关联获取(lazy association fetch)混淆了 (译注:这里指不要把lazy simple property和lazy association fetch混淆了).

推荐的替代方案是使用EJB-QL或者Criteria查询的投影(projection)功能.

Hibernate和EJB3都支持所有基本类型的属性映射. 这些基本类型包括所有的Java基本类型,及其各自的wrapper类和serializable类. Hibernate Annotations还支持将内置的枚举类型映射到一个顺序列(保存了相应的序列值) 或一个字符串类型的列(保存相应的字符串).默认是保存枚举的序列值, 但是你可以通过 @Enumerated 注解来进行调整(见上面例子中的note属性).

在核心的Java API中并没有定义时间精度(temporal precision). 因此处理时间类型数据时,你还需要定义将其存储在数据库中所预期的精度. 在数据库中,表示时间类型的数据有 DATE , TIME , 和 TIMESTAMP 三种精度(即单纯的日期,时间,或者两者兼备). 可使用 @Temporal 注解来调整精度.

@Lob 注解表示属性将被持久化为Blob或者Clob类型, 具体取决于属性的类型, java.sql.Clob , Character[] , char[] java.lang.String 这些类型的属性都被持久化为Clob类型, 而 java.sql.Blob , Byte[] , byte[] 和 serializable类型则被持久化为Blob类型.

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

如果某个属性实现了 java.io.Serializable 同时也不是基本类型, 并且没有在该属性上使用 @Lob 注解, 那么Hibernate将使用自带的 serializable 类型.

使用 @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="columnName";                                (1)
    boolean unique() default false;                   (2)
    boolean nullable() default true;                  (3)
    boolean insertable() default true;                (4)
    boolean updatable() default true;                 (5)
    String columnDefinition() default "";             (6)
    String table() default "";                        (7)
    int length() default 255;                         (8)
    int precision() default 0; // decimal precision   (9)
    int scale() default 0; // decimal scale
(1)

name 可选,列名(默认值是属性名)

(2)

unique 可选,是否在该列上设置唯一约束(默认值false)

(3)

nullable 可选,是否设置该列的值可以为空(默认值false)

(4)

insertable 可选,该列是否作为生成的insert语句中的一个列(默认值true)

(5)

updatable 可选,该列是否作为生成的update语句中的一个列(默认值true)

(6)

columnDefinition 可选: 为这个特定列覆盖SQL DDL片段 (这可能导致无法在不同数据库间移植)

(7)

table 可选,定义对应的表(默认为主表)

(8)

length 可选,列长度(默认值255)

(8)

precision 可选,列十进制精度(decimal precision)(默认值0)

(10)

scale 可选,如果列十进制数值范围(decimal scale)可用,在此设置(默认值0)

在实体中可以定义一个嵌入式组件(embedded component), 甚至覆盖该实体中原有的列映射. 组件类必须在类一级定义 @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; }
            

嵌入式对象继承其所属实体中定义的访问类型 (注意:这可以通过使用Hibernate提供的 @AccessType 注解来覆盖原有值)(请参考 Hibernate Annotation Extensions ).

在上面的例子中,实体bean Person 有两个组件属性, 分别是 homeAddress bornIn . 我们可以看到 homeAddress 属性并没有注解. 但是Hibernate自动检测其对应的Address类中的 @Embeddable 注解, 并将其看作一个持久化组件.对于Country中已映射的属性, 则使用 @Embedded @AttributeOverride 注解来覆盖原来映射的列名. 正如你所看到的, Address 对象中还内嵌了 Country 对象, 这里和 homeAddress 一样使用了Hibernate和EJB3自动检测机制. 目前EJB3规范还不支持覆盖多层嵌套(即嵌入式对象中还包括其他嵌入式对象)的列映射. 不过Hibernate通过在表达式中使用"."符号表达式提供了对此特征的支持.

    @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注解支持很多EJB3规范中没有明确定义的特性. 例如,可以在嵌入式对象上添加 @MappedSuperclass 注解, 这样可以将其父类的属性持久(详情请查阅 @MappedSuperclass ).

Hibernate现在支持在嵌入式对象中使用关联注解(如 @*ToOne @*ToMany ). 而EJB3规范尚不支持这样的用法。你可以使用 @AssociationOverride 注解来覆写关联列.

在同一个实体中使用两个同类型的嵌入对象, 其默认列名是无效的:至少要对其中一个进行明确声明. Hibernate在这方面走在了EJB3规范的前面, Hibernate提供了 NamingStrategy , 在使用Hibernate时, 通过 NamingStrategy 你可以对默认的机制进行扩展. DefaultComponentSafeNamingStrategy 在默认的EJB3NamingStrategy上进行了小小的提升, 允许在同一实体中使用两个同类型的嵌入对象而无须额外的声明.

和EJB3规范相比,Hibernate提供了更多的id生成器.详情请查阅 Hibernate Annotation Extensions .

下面的例子展示了使用SEQ_STORE配置的sequence生成器

@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE")
public Integer getId() { ... }
         

下面这个例子使用的是identity生成器

@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
public Long getId() { ... }
         

AUTO 生成器适用于可移植的应用(在多个DB间切换). 多个 @Id 可以共享同一个identifier生成器,只要把generator属性设成相同的值就可以了. 通过 @SequenceGenerator @TableGenerator ,你可以配置不同的identifier生成器. 每一个identifier生成器都有自己的适用范围,可以是应用级(application level)和类一级(class level). 类一级的生成器在外部是不可见的, 而且类一级的生成器可以覆盖应用级的生成器. 应用级的生成器则定义在包一级(package level)(如 package-info.java ):

@javax.persistence.TableGenerator(
    name="EMP_GEN",
    table="GENERATOR_TABLE",
    pkColumnName = "key",
    valueColumnName = "hi"
    pkColumnValue="EMP",
    allocationSize=20
@javax.persistence.SequenceGenerator(
    name="SEQ_GEN",
    sequenceName="my_sequence"
package org.hibernate.test.metadata;
	  如果在org.hibernate.test.metadata包下面的
	  package-info.java文件用于初始化EJB配置,
	  那么该文件中定义的 EMP_GENSEQ_GEN都是应用级的生成器. 
      EMP_GEN定义了一个使用hilo算法
	  (max_lo为20)的id生成器(该生成器将id的信息存在数据库的某个表中.).
	  id的hi值保存在GENERATOR_TABLE中.
      在该表中 pkColumnName"key"等价于
	  pkColumnValue "EMP",
      而valueColumnName "hi"中存储的是下一个要使用的最大值.
	  

SEQ_GEN则定义了一个sequence 生成器, 其对应的sequence名为 my_sequence. 注意目前Hibernate Annotations还不支持sequence 生成器中的 initialValueallocationSize参数.

下面这个例子展示了定义在类范围(class scope)的sequence生成器:

@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; }
         

在这个例子中,Store类使用名为my_sequence的sequence,并且SEQ_STORE 生成器对于其他类是不可见的. 注意在org.hibernate.test.metadata.id包下的测试代码有更多演示Hibernate Annotations用法的例子..

下面是定义组合主键的几种语法:

  • 将组件类注解为@Embeddable,并将组件的属性注解为@Id 将组件的属性注解为@EmbeddedId 将类注解为@IdClass,并将该实体中所有属于主键的属性都注解为@Id

对于EJB2的开发人员来说 @IdClass是很常见的, 但是对于Hibernate的用户来说就是一个崭新的用法. 组合主键类对应了一个实体类中的多个字段或属性, 而且主键类中用于定义主键的字段或属性和 实体类中对应的字段或属性在类型上必须一致.下面我们看一个例子:

@Entity
@IdClass(FootballerPk.class)
public class Footballer {
    //part of the id key
    @Id public String getFirstname() {
        return firstname;
    public void setFirstname(String firstname) {
        this.firstname = firstname;
    //part of the id key
    @Id public String getLastname() {
        return lastname;
    public void setLastname(String lastname) {
        this.lastname = lastname;
    public String getClub() {
        return club;
    public void setClub(String club) {
        this.club = club;
    //appropriate equals() and hashCode() implementation
@Embeddable
public class FootballerPk implements Serializable {
    //same name and type as in Footballer
    public String getFirstname() {
        return firstname;
    public void setFirstname(String firstname) {
        this.firstname = firstname;
    //same name and type as in Footballer
    public String getLastname() {
        return lastname;
    public void setLastname(String lastname) {
        this.lastname = lastname;
    //appropriate equals() and hashCode() implementation

如上, @IdClass指向对应的主键类.

Hibernate支持在组合标识符中定义关联(就像使用普通的注解一样),而EJB3规范并不支持此类用法.

@Entity
@AssociationOverride( name="id.channel", joinColumns = @JoinColumn(name="chan_id") )
public class TvMagazin {
    @EmbeddedId public TvMagazinPk id;
    @Temporal(TemporalType.TIME) Date time;
@Embeddable
public class TvMagazinPk implements Serializable {
    @ManyToOne
    public Channel channel;
    public String name;
    @ManyToOne
    public Presenter presenter;

EJB3支持三种类型的继承映射: