• EJB3.0-JPA实体的注解规范以及Hibernate特有的扩展

  • 作者:yongtree  来源地址:duduwolf  发表时间:2009-06-16  点击数:
    5
  • 本章内容覆盖了EJB3.0(也就是JPA)实体的注解规范以及Hibernate特有的扩展.
     

        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;

        @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的位置来判断访问类型.

            @Table是类一级的注解,
      通过 @Table注解可以为实体bean映射指定表(table),目录(catalog)和schema的名字.
      如果没有定义 @Table,那么系统自动使用默认值:实体的短类名(不附带包名).

           
    @Entity
    @Table(name="tbl_sky")
    public class Sky implements Serializable {
    ...
               

            @Table元素包括了一个 schema
      和一个 catalog属性,如果需要可以指定相应的值.
      结合使用 @UniqueConstraint注解可以定义表的唯一约束(unique constraint)
      (对于绑定到单列的唯一约束,请参考 @Column注解)
      

            @Table(name="tbl_sky",
          uniqueConstraints = {@UniqueConstraint(columnNames={"month", "day"})}
      )

            上面这个例子中,在month和day这两个字段上定义唯一约束.
      注意 columnNames数组中的值指的是逻辑列名.

            Hibernate在NamingStrategy的实现中定义了逻辑列名.
      默认的EJB3命名策略将物理字段名当作逻辑字段名来使用.
      注意该字段名和它对应的属性名可能不同(如果字段名是显式指定的话).
      除非你重写了NamingStrategy,否则不用担心这些区别..

            你可以在实体bean中使用 @Version注解,通过这种方式可添加对乐观锁定的支持:

           
    @Entity
    public class Flight implements Serializable {
    ...
        @Version
        @Column(name="OPTLOCK")
        public Integer getVersion() { ... }
    }          

            上面这个例子中,version属性将映射到 OPTLOCK列,
      entity manager使用该字段来检测更新冲突(防止更新丢失,请参考last-commit-wins策略).

            根据EJB3规范,version列可以是numeric类型(推荐方式)也可以是timestamp类型.
      Hibernate支持任何自定义类型,只要该类型实现了UserVersionType.
          
               

            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(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混淆了).
       
              为了启用属性级的延迟获取,你的类必须经过特殊处理(instrumented):
        字节码将被织入原始类中来实现延迟获取功能,
        详情参考Hibernate参考文档.如果不对类文件进行字节码特殊处理,
        那么属性级的延迟获取将被忽略.
           

            推荐的替代方案是使用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类型.

           
    @Lob
    public String getFullText() {
        return fullText;
    }

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

            如果某个属性实现了java.io.Serializable同时也不是基本类型,
      并且没有在该属性上使用 @Lob注解,
      那么Hibernate将使用自带的 serializable类型.
      
               使用 @Column 注解可将属性映射到列.
      使用该注解来覆盖默认值(关于默认值请参考EJB3规范).
      在属性级使用该注解的方式如下:

           
             
                不进行注解
             

             
                @Basic一起使用
             

             
                @Version一起使用
             

             
                @Lob一起使用
             

             
                @Temporal一起使用
             

             
               
                @org.hibernate.annotations.CollectionOfElements一起使用
                (只针对Hibernate )
             
           


           
    @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";
        boolean unique() default false;
        boolean nullable() default true;
        boolean insertable() default true;
        boolean updatable() default true;
        String columnDefinition() default "";
        String table() default "";
        int length() default 255;
        int precision() default 0; // decimal precision
        int scale() default 0; // decimal scale

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

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

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

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

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

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

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

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

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

               
                  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注解来覆盖原有值)(请参考         linkend="entity-hibspec" />).

            在上面的例子中,实体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上进行了小小的提升,
      允许在同一实体中使用两个同类型的嵌入对象而无须额外的声明.

            如果某属性没有注解,该属性将遵守下面的规则:

           
             
                 如果属性为单一类型,则映射为@Basic
             

             
                 否则,如果属性对应的类型定义了@Embeddable注解,则映射为@Embedded
             

             
                 否则,如果属性对应的类型实现了Serializable,
        则属性被映射为@Basic并在一个列中保存该对象的serialized版本
             

             
                否则,如果该属性的类型为java.sql.Clob 或 java.sql.Blob,则作为@Lob并映射到适当的LobType.
              
                           xreflabel="Mapping identifier properties">
         

          使用 @Id注解可以将实体bean中的某个属性定义为标识符(identifier).
       该属性的值可以通过应用自身进行设置,
       也可以通过Hiberante生成(推荐).
       使用 @GeneratedValue注解可以定义该标识符的生成策略:
      

         
           
               AUTO - 可以是identity column类型,或者sequence类型或者table类型,取决于不同的底层数据库.
           

           
               TABLE - 使用表保存id值
           

           
               IDENTITY - identity column
           

           
               SEQUENCE - sequence
           
         

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


          下面的例子展示了使用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).
       类一级的生成器在外部是不可见的,而且类一级的生成器可以覆盖应用级的生成器.
       应用级的生成器则定义在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_GEN
       和 SEQ_GEN都是应用级的生成器.
          EMP_GEN定义了一个使用hilo算法
       (max_lo为20)的id生成器(该生成器将id的信息存在数据库的某个表中.).
       id的hi值保存在 GENERATOR_TABLE中.
          在该表中 pkColumnName"key"等价于
       pkColumnValue " EMP",
          而 valueColumnName " hi"中存储的是下一个要使用的最大值.
      

          SEQ_GEN定义了一个sequence生成器,
          其使用名为 my_sequence的sequence.
          该hilo算法基于sequence,该sequence分配的大小为20.
          注意,现在这个版本还不能处理sequence生成器的 initialValue属性.
          默认分配的大小为50,因此如果你打算使用sequence,并且希望每次都重新获取新的值,务必将
          分配的大小设置为1.

         
            EJB3.0规范已经不再支持Package级别的定义. 但是你仍然可以在包上使用
            @GenericGenerator注解(详情请参考        linkend="entity-hibspec-identifier" />).
         

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

          下面这个例子展示了定义在类范围(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支持三种类型的继承映射:

         
           
                每个类一张表(Table per class)策略: 在Hibernate中对应<union-class>元素:
           

           
         每个类层次结构一张表(Single table per class hierarchy)策略:在Hibernate中对应<subclass>元素
           

           
         连接的子类(Joined subclasses)策略:在Hibernate中对应 <joined-subclass>元素
           
         

         
          你可以用 @Inheritance注解来定义所选择的策略.
          这个注解需要在每个类层次结构(class hierarchy) 最顶端的实体类上使用.
         

         
            目前还不支持在接口上进行注解.
         

         
           

           
      这种策略有很多缺点(例如:多态查询和关联),EJB3规范, Hibernate参考手册,
      Hibernate in Action,以及其他许多地方都对此进行了描述和解释.
      Hibernate使用 SQL UNION查询来实现这种策略.
      通常使用场合是在一个继承层次结构的顶端:

           
    @Entity
    @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
    public class Flight implements Serializable {
               

           
      这种策略支持双向的一对多关联.
      这里不支持 IDENTITY生成器策略,因为id必须在多个表间共享.
      当然,一旦使用这种策略就意味着你不能使用
       AUTO 生成器和 IDENTITY生成器.
      
         

         
           

            整个继承层次结构中的父类和子类的所有属性都映射到同一个表中,
      他们的实例通过一个辨别符(discriminator)列来区分.:

           
    @Entity
    @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(
        name="planetype",
        discriminatorType=DiscriminatorType.STRING
    )
    @DiscriminatorValue("Plane")
    public class Plane { ... }

    @Entity
    @DiscriminatorValue("A320")
    public class A320 extends Plane { ... }
               

            在上面这个例子中,Plane是父类,在这个类里面将继承策略定义为
       InheritanceType.SINGLE_TABLE,并通过
       @DiscriminatorColumn注解定义了辨别符列(还可以定义辨别符的类型).
      最后,对于继承层次结构中的每个类, @DiscriminatorValue注解指定了用来辨别该类的值.
      辨别符列的名字默认为 DTYPE,其默认值为实体名(在 @Entity.name中定义),其类型
      为DiscriminatorType.STRING.
       A320是子类,如果不想使用默认的辨别符,只需要指定相应的值即可.
      其他的如继承策略,辨别标志字段的类型都是自动设定的.

       @Inheritance 和
            @DiscriminatorColumn 注解只能用于实体层次结构的顶端.

         

         
           

            当每个子类映射到一个表时, @PrimaryKeyJoinColumn
      和 @PrimaryKeyJoinColumns
      注解定义了每个子类表关联到父类表的主键:

           
    @Entity
    @Inheritance(strategy=InheritanceType.JOINED)
    public class Boat implements Serializable { ... }

    @Entity
    public class Ferry extends Boat { ... }

    @Entity
    @PrimaryKeyJoinColumn(name="BOAT_ID")
    public class AmericaCupClass  extends Boat { ... }
               

            以上所有实体都使用了 JOINED策略,
       Ferry表和 Boat表使用同名的主键.
      而 AmericaCupClass表和 Boat表使用了条件
      Boat.id = AmericaCupClass.BOAT_ID进行关联.
      
                  有时候通过一个(技术上或业务上)父类共享一些公共属性是很有用的,
      同时还不用将该父类作为映射的实体(也就是该实体没有对应的表).
      这个时候你需要使用 @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表的形式出现,
      该表拥有 id, lastUpdate 和
       lastUpdater三个列.父类中的属性映射将复制到其子类实体.
      注意这种情况下的父类不再处在继承层次结构的顶端.

           

              注意,没有注解为 @MappedSuperclass的父类中的属性将被忽略.
           

                      除非显式使用Hibernate annotation中的 @AccessType注解,
        否则将从继承层次结构的根实体中继承访问类型(包括字段或方法)
           

           

              这对于 @Embeddable对象的父类中的属性持久化同样有效.
        只需要使用 @MappedSuperclass注解即可
        (虽然这种方式不会纳入EJB3标准)

                     可以将处在在映射继承层次结构的中间位置的类注解为 @MappedSuperclass.
            

              在继承层次结构中任何没有被注解为 @MappedSuperclass
        或 @Entity的类都将被忽略.
           

      
      你可以通过 @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列.而名为propulsion的关联则保存在 fld_propulsion_fk外间列.

            你可以为 @Entity和 @MappedSuperclass注解的类
      以及那些对象为 @Embeddable的属性定义
       @AttributeOverride和 @AssociationOverride.

                 

       使用 @OneToOne注解可以建立实体bean之间的一对一的关联.
      一对一关联有三种情况:
      一是关联的实体都共享同样的主键,
      二是其中一个实体通过外键关联到另一个实体的主键
      (注意要模拟一对一关联必须在外键列上添加唯一约束).
      三是通过关联表来保存两个实体之间的连接关系
      (注意要模拟一对一关联必须在每一个外键上添加唯一约束).

            首先,我们通过共享主键来进行一对一关联映射:

           
    @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 通过 Customer
      表中名为的 passport_fk 外键列和 Passport关联.
       @JoinColumn注解定义了联接列(join column).
      该注解和 @Column注解有点类似,
      但是多了一个名为 referencedColumnName的参数.
      该参数定义了所关联目标实体中的联接列.
      注意,当 referencedColumnName关联到非主键列的时候,
      关联的目标类必须实现Serializable,
      还要注意的是所映射的属性对应单个列(否则映射无效).
      

            一对一关联可能是双向的.在双向关联中,
      有且仅有一端是作为主体(owner)端存在的:主体端负责维护联接列(即更新).
      对于不需要维护这种关系的从表则通过mappedBy属性进行声明.
       mappedBy的值指向主体的关联属性.
      在上面这个例子中, mappedBy的值为 passport.
      最后,不必也不能再在被关联端(owned side)定义联接列了,因为已经在主体端进行了声明.

       如果在主体没有声明 @JoinColumn,系统自动进行处理:
      在主表(owner table)中将创建联接列,
      列名为:主体的关联属性名+下划线+被关联端的主键列名.
      在上面这个例子中是 passport_id,
      因为 Customer中关联属性名为 passport,
       Passport的主键是 id.

            The third possibility (using an association table) is very
            exotic.

            第三种方式也许是最另类的(通过关联表).

           
    @Entity
    public class Customer implements Serializable {
        @OneToOne(cascade = CascadeType.ALL)
        @JoinTable(name = "CustomerPassports"
            joinColumns = @JoinColumn(name="customer_fk"),
            inverseJoinColumns = @JoinColumns(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是可选的,关联字段默认值和一对一
      (one to one)关联的情况相似,
      列名为:主体的关联属性名+下划线+被关联端的主键列名.
      在这个例子中是 company_id,
      因为关联的属性是 company,
       Company的主键是 id.

       @ManyToOne注解有一个名为 targetEntity的参数,
      该参数定义了目标实体名.通常不需要定义该参数,
      因为在大部分情况下默认值(表示关联关系的属性类型)就可以很好的满足要求了.
      不过下面这种情况下这个参数就显得有意义了:使用接口作为返回值而不是常见的实体.

           
    @Entity()
    public class Flight implements Serializable {
        @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE},             role="bold">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 = @JoinColumns(name="COMP_ID")
        )
        public Company getCompany() {
            return company;
        }
        ...
    }
                       

                           revision="1">
             

              你可以对 Collection , List
        (指有序列表, 而不是索引列表),
        Map和Set这几种类型进行映射.
        EJB3规范定义了怎么样使用 @javax.persistence.OrderBy
        注解来对有序列表进行映射:
        该注解接受的参数格式:用逗号隔开的(目标实体)属性名及排序指令,
        如firstname asc, age desc,如果该参数为空,则默认以id对该集合进行排序.
               如果某个集合在数据库中对应一个关联表(association table)的话,你不能在这个集合属性上面使用@OrderBy注解.
        对于这种情况的处理方法,请参考.
        EJB3 允许你利用目标实体的一个属性作为Map的key,
        这个属性可以用 @MapKey(name="myProperty")来声明.
        如果使用 @MapKey注解的时候不提供属性名,
        系统默认使用目标实体的主键.
        map的key使用和属性相同的列:不需要为map key定义专用的列,因为map key实际上就表达了一个目标属性.
        注意一旦加载,key不再和属性保持同步,
        也就是说,如果你改变了该属性的值,在你的Java模型中的key不会自动更新
        (请参考).
        很多人被 <map>和 @MapKey弄糊涂了.
        其他它们有两点区别. @MapKey目前还有一些限制,详情请查看论坛或者
        我们的JIRA缺陷系统.


        注意一旦加载,key不再和属性保持同步,
        也就是说,如果你改变了该属性的值,在你的Java模型中的key不会自动更新.
        (Hibernate 3中Map支持的方式在当前的发布版中还未得到支持).

              Hibernate将集合分以下几类.

             

             

     

        
        从上面可以明确地看到,没有@org.hibernate.annotations.IndexColumn
        注解的java.util.List集合将被看作bag类.
       

              EJB3规范不支持原始类型,核心类型,嵌入式对象的集合.但是Hibernate对此提供了支持
        (详情参考 ).

              @Entity public class City {
        @OneToMany(mappedBy="city")
        @OrderBy("streetName")
        public List<Street> getStreets() {
            return streets;
        }
    ...
    }

    @Entity public class Street {
        public String getStreetName() {
            return streetName;
        }

        @ManyToOne
        public City getCity() {
            return city;
        }
        ...
    }


    @Entity
    public class Software {
        @OneToMany(mappedBy="software")
        @MapKey(name="codeName")
        public Map<String, Version> getVersions() {
            return versions;
        }
    ...
    }

    @Entity
    @Table(name="tbl_version")
    public class Version {
        public String getCodeName() {...}

        @ManyToOne
        public Software getSoftware() { ... }
    ...
    }

              上面这个例子中, City
        中包括了以 streetName排序的 Street的集合.
        而 Software中包括了以 codeName作为
        key和以 Version作为值的Map.

        除非集合为generic类型,否则你需要指定 targetEntity.
        这个注解属性接受的参数为目标实体的class.
           

                           revision="2">
             

              在属性级使用 @OneToMany注解可定义一对多关联.一对多关联可以是双向关联.

             
               

                在EJB3规范中多对一这端几乎总是双向关联中的主体(owner)端,
       而一对多这端的关联注解为 @OneToMany( mappedBy=...
                )

                @Entity
    public class Troop {
        @OneToMany(mappedBy="troop")
        public Set<Soldier> getSoldiers() {
        ...
    }

    @Entity
    public class Soldier {
        @ManyToOne
        @JoinColumn(name="troop_fk")
        public Troop getTroop() {
        ...
    }             

        Troop 通过 troop
       属性和 Soldier建立了一对多的双向关联.
       在 mappedBy端不必也不能再定义任何物理映射

                对于一对多的双向映射,如果要一对多这一端维护关联关系,
       你需要删除 mappedBy元素并将多对一这端的
        @JoinColumn的insertable和updatable设置为false.
       很明显,这种方案不会得到什么明显的优化,而且还会增加一些附加的UPDATE语句.

                @Entity
    public class Troop {
        @OneToMany
        @JoinColumn(name="troop_fk") //we need to duplicate the physical information
        public Set<Soldier> getSoldiers() {
        ...
    }

    @Entity
    public class Soldier {
        @ManyToOne
        @JoinColumn(name="troop_fk", insertable=false, updatable=false)
        public Troop getTroop() {
        ...
    }
             

             
               

        通过在被拥有的实体端(owned entity)增加一个外键列来实现一对多单向关联是很少见的,也是不推荐的.
       我们强烈建议通过一个联接表(join table)来实现这种关联(下一节会对此进行解释).
       可以通过 @JoinColumn注解来描述这种单向关联关系.

               
    @Entity
    public class Customer implements Serializable {
        @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
        @JoinColumn(name="CUST_ID")
        public Set<Ticket> getTickets() {
        ...
    }

    @Entity
    public class Ticket implements Serializable {
        ... //no bidir
    }
                  

        Customer 通过
        CUST_ID列和 Ticket 建立了单向关联关系.

                          通过联接表处理单向一对多关联是首选方式.这种关联通过 @JoinTable注解来进行描述.

               
    @Entity
    public class Trainer {
        @OneToMany
        @JoinTable(
                name="TrainedMonkeys",
                joinColumns = { @JoinColumn( name="trainer_id") },
                inverseJoinColumns = @JoinColumn( name="monkey_id")
        )
        public Set<Monkey> getTrainedMonkeys() {
        ...
    }

    @Entity
    public class Monkey {
        ... //no bidir
    }
                  

        上面这个例子中, Trainer通过
       TrainedMonkeys表和
       Monkey 建立了单向关联.
       其中外键 trainer_id关联到 Trainer
       ( joinColumns),
       而外键 monkey_id关联到 Monkey
       ( inversejoinColumns).
             

                               revision="1">
               

                通过联接表来建立单向一对多关联不需要描述任何物理映射.
                表名由以下三个部分组成:主表(owner table)表名+下划线+从表(the other side table)表名.
                指向主表的外键名:主表表名+下划线+主表主键列名
                指向从表的外键名:主表所对应实体的属性名+下划线+从表主键列名
                指向从表的外键定义为唯一约束,用来表示一对多的关联关系.

               
    @Entity
    public class Trainer {
        @OneToMany
        public Set<Tiger> getTrainedTigers() {
        ...
    }

    @Entity
    public class Tiger {
        ... //no bidir
    }
                  

                上面这个例子中,Trainer和Tiger
       通过联接表 Trainer_Tiger建立单向关联关系,
       其中外键 trainer_id关联到 Trainer
       (主表表名, _(下划线), trainer id),
       而外键 trainedTigers_id关联到 Tiger
       (属性名称,  _(下划线), Tiger表的主键列名).

             
           

                           revision="">
             

             
               

                你可以通过 @ManyToMany注解可定义的多对多关联.
       同时,你也需要通过注解 @JoinTable描述关联表和关联条件.
       如果是双向关联,其中一段必须定义为owner,另一端必须定义为inverse(在对关联表进行更新操作时这一端将被忽略):

               
    @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注解,该注解定义了联接表的表名,
       联接列数组(注解中定义数组的格式为{ A, B, C }),
       以及inverse联接列数组.
       后者是关联表中关联到Employee主键的列(the "other side").

                正如前面所示,被关联端不必也不能描述物理映射:
       只需要一个简单的 mappedBy参数,该参数包含了主体端的属性名,这样就绑定双方的关系.
             

                  和其他许多注解一样,在多对多关联中很多值是自动生成.
       当双向多对多关联中没有定义任何物理映射时,Hibernate根据以下规则生成相应的值.
       关联表名:主表表名+_下划线+从表表名,
       关联到主表的外键名:主表名+_下划线+主表中的主键列名.
       关联到从表的外键名:主表中用于关联的属性名+_下划线+从表的主键列名.
       以上规则对于双向一对多关联同样有效.

               
    @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表.

                当双向多对多关联中没有定义任何物理映射时, Hibernate根据以下规则生成相应的值
                关联表名: :主表表名+_下划线+从表表名,
                关联到主表的外键名:从表用于关联的属性名+_下划线+主表中的主键列名.
                关联到从表的外键名:主表用于关联的属性名+_下划线+从表的主键列名.
                以上规则对于双向一对多关联同样有效.

               
    @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表.

            
           

            也许你已经注意到了 cascade属性接受的值为CascadeType数组.
      在EJB3中的cascade的概念和Hibernate中的传播性持久化以及cascade操作非常类似,
      但是在语义上有细微的区别,支持的cascade类型也有点区别:

           
             
                 CascadeType.PERSIST: 如果一个实体是受管状态, 或者当persist()函数被调用时, 触发级联创建(create)操作
             

             
                 CascadeType.MERGE: 如果一个实体是受管状态, 或者当merge()函数被调用时, 触发级联合并(merge)操作
             

             
                 CascadeType.REMOVE: 当delete()函数被调用时, 触发级联删除(remove)操作
             

             
                 CascadeType.REFRESH: 当refresh()函数被调用时, 触发级联更新(refresh)操作
             

             
                 CascadeType.ALL: 以上全部
             
           

       关于cascading, create/merge的语义请参考EJB3规范的6.3章节.
         

         
           

            通过Hibernate你可以获得直接或者延迟获取关联实体的功能.
       fetch参数可以设置为 FetchType.LAZY
      或者 FetchType.EAGER.
       EAGER通过 outer join select直接获取关联的对象,
      而 LAZY(默认值)在第一次访问关联对象的时候才会触发相应的select操作.
      EJBQL提供了 fetch关键字,该关键字可以在进行特殊查询的时候覆盖默认值.
      这对于提高性能来说非常有效,应该根据实际的用例来判断是否选择fetch关键字.
          
             

          组合主键使用一个可嵌入的类作为主键表示,因此你需要使用 @Id
       和 @Embeddable两个注解.
       还有一种方式是使用 @EmbeddedId注解.注意所依赖的类必须实现
       serializable以及实现equals()/hashCode()方法.
       你也可以如一章中描述的办法使用 @IdClass注解.

         
    @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 注解默认继承了其所属实体的访问类型,
       除非显式使用了Hibernate的 @AccessType注解(这个注解不是EJB3标准的一部分).
       而 @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显式使用方式.
       

       
         

          使用类一级的 @SecondaryTable 或
        @SecondaryTables 注解可以实现单个实体到多个表的映射.
       使用 @Column  或者  @JoinColumn 
       注解中的 table 参数可指定某个列所属的特定表.

         
    @Entity
    @Table(name="MainCat")
    @SecondaryTables({
        @SecondaryTable(name="Cat1", pkJoinColumns={
            @PrimaryKeyJoinColumn(name="cat_id", referencedColumnName="id")
        ),
        @SecondaryTable(name="Cat2", uniqueConstraints={@UniqueConstraint(columnNames={"storyPart2"})})
    })
    public class Cat implements Serializable {

        private Integer id;
        private String name;
        private String storyPart1;
        private String storyPart2;

        @Id @GeneratedValue
        public Integer getId() {
            return id;
        }

        public String getName() {
            return name;
        }
       
        @Column(table="Cat1")
        public String getStoryPart1() {
            return storyPart1;
        }

        @Column(table="Cat2")
        public String getStoryPart2() {
            return storyPart2;
        }
     

          在上面这个例子中, name保存在 MainCat表中,
       storyPart1保存在 Cat1表中,
       storyPart2保存在 Cat2表中.
       Cat1表通过外键 cat_id和 MainCat表关联,
       Cat2表通过 id列和 MainCat表关联
       (和 MainCat的 id列同名).
       对 storyPart2列还定义了唯一约束.

          在JBoss EJB 3指南和Hibernate Annotations单元测试代码中还有更多的例子.
       
     

     
       

                   revision="1">
         

          使用注解还可以映射EJBQL/HQL查询.
       @NamedQuery 和 @NamedQueries注解可使用在类和JPA XML文件中.
       但是它们的定义在session factory/entity manager factory范围中是都可见的.
       命名式查询通过它的名字和实际的查询字符串来定义.

          <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();
            ...
        }
        ...
    }
           

          还可以通过定义 QueryHint 数组的 hints
       属性为查询提供一些hint信息.

          下面是目前可以使用的一些Hibernate hint:

         

         

     


       

       
         

          你还可以映射本地化查询(也就是普通SQL查询).
       不过这需要你使用 @SqlResultSetMapping注解来描述SQL的resultset的结构
       (如果你打算定义多个结果集映射,可是使用 @SqlResultSetMappings).
       @SqlResultSetMapping和 @NamedQuery,
       @SqlResultSetMapping一样,可以定义在类和JPA XML文件中.
       但是 @SqlResultSetMapping的作用域为应用级.
      
      
      
       下面我们会看到, @NamedNativeQuery 注解中
       resultSetMapping参数值为 @SqlResultSetMapping的名字.
       结果集映射定义了通过本地化查询返回值和实体的映射.
       该实体中的每一个字段都绑定到SQL结果集中的某个列上.
       该实体的所有字段包括子类的所有字段以及
       关联实体的外键列都必须在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",             role="bold">resultSetMapping="joinMapping")
    @SqlResultSetMapping(name="joinMapping", entities={
        @EntityResult(entityClass=org.hibernate.test.annotations.query.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结果集映射对应.
       该映射返回两个实体,分别为 Night
       和 Area,其中每个属性都和一个列关联,
       列名通过查询获取.下面我们看一个隐式声明属性和列映射关系的例子.

          @Entity
    @SqlResultSetMapping(name="implicit", entities=@EntityResult(entityClass=org.hibernate.test.annotations.query.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=org.hibernate.test.annotations.query.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;
        }
    }
     

         

       观察dimension属性你会发现Hibernate支持用"."符号来表示嵌入式对象.
      EJB3实现不必支持这个特征,但是我们做到了:-)
         

          如果查询返回的是单个实体,或者你打算使用系统默认的映射,
       这种情况下可以不使用 resultSetMapping
       而是使用 resultClass属性:

          @NamedNativeQuery(name="implicitSample", query="select * from SpaceShip",
        resultClass=SpaceShip.class)
    public class SpaceShip {

          某些本地查询返回的是scalar值,例如报表查询.
       你可以通过 @ColumnResult将其映射到
       @SqlResultsetMapping上.
       甚至还可以在同一个本地查询的结果中混合实体和scalar类型(不过这种情况比较少见).

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

          本地查询中还有另外一个hint属性:
       org.hibernate.callable.
       这个属性的布尔变量值表明这个查询是否是一个存储过程.
       
     

     
       

        Hibernate 3.1 提供了多种附加的注解,这些注解可以与EJB3的实体混合/匹配使用.
        他们被设计成EJB3注解的自然扩展.

        为了强化EJB3的能力,Hibernate提供了与其自身特性相吻合的特殊注解.
        org.hibernate.annotations包已包含了所有的这些注解扩展.

       
         

          你可以在EJB3规范所能提供的能力之外,就Hibernate对实体所做的一些操作进行优化.

          @org.hibernate.annotations.Entity
          追加了可能需要的额外的元数据,
          而这些元数据超出了标准 @Entity 中所定义的元数据.
      
             
                 mutable: 此实体是否为可变的
             

             
                 dynamicInsert: 用动态SQL新增
             

             
                 dynamicUpdate: 用动态SQL更新
             

             
                 selectBeforeUpdate: 指明Hibernate从不运行SQL UPDATE除非能确定对象的确已被修改
             

             
                 polymorphism: (指出)实体多态是PolymorphismType.IMPLICIT(默认)还是PolymorphismType.EXPLICIT
       

       
                 persister:允许对默认持久实现(persister implementation)的覆盖
                

             
                 optimisticLock: 乐观锁策略(OptimisticLockType.VERSION, OptimisticLockType.NONE, OptimisticLockType.DIRTY或OptimisticLockType.ALL)
             
           
      

         
      
       
        @javax.persistence.Entity仍是必选的(mandatory),
        @org.hibernate.annotations.Entity不是取代品.
       
           
      

          以下是一些附加的Hibernate注解扩展:

          @org.hibernate.annotations.BatchSize
      允许你定义批量获取该实体的实例数量(如: @BatchSize(size=4)).
          当加载一特定的实体时,Hibernate将加载在持久上下文中未经初始化的同类型实体,直至批量数量(上限).

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

          @org.hibernate.annotations.Where
      定义了当获取类实例时所用的SQL WHERE子句(该SQL WHERE子句为可选).

          @org.hibernate.annotations.Check
          定义了在DDL语句中定义的合法性检查约束(该约束为可选).

          @OnDelete(action=OnDeleteAction.CASCADE)
          定义于被连接的子类(joined subclass):在删除时使用SQL级连删除,而非通常的Hibernate删除机制.

          @Table(name="tableName", indexes = {
          @Index(name="index1", columnNames={"column1", "column2"} ) } )
       在 tableName表的列上创建定义好的索引.
       该注解可以被应用于关键表或者是其他次要的表.
          @Tables 注解允许你在不同的表上应用索引.
          此注解预期在使用
          @javax.persistence.Table或
          @javax.persistence.SecondaryTable的地方中出现.

         
           
       @org.hibernate.annotations.Table 是对
            @javax.persistence.Table的补充而不是它的替代品.
      特别是当你打算改变表名的默认值的时候,你必须使用 @javax.persistence.Table,
      而不是 @org.hibernate.annotations.Table.
         

                @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" } ) } )
    public class Forest { ... } @Entity
    @Inheritance(
        strategy=InheritanceType.JOINED
    )
    public class Vegetable { ... }

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

       
         

          @org.hibernate.annotations.GenericGenerator
          允许你定义一个Hibernate特定的id生成器.

          @Id @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    public String getId() {

    @Id @GeneratedValue(generator="hibseq")
    @GenericGenerator(name="hibseq", strategy = "seqhilo",
        parameters = {
            @Parameter(name="max_lo", value = "5"),
            @Parameter(name="sequence", value="heybabyhey")
        }
    )
    public Integer getId() {

      strategy可以是Hibernate3生成器策略的简称,
     或者是一个IdentifierGenerator实现的(带包路径的)全限定类名.
     你可以通过 parameters属性增加一些参数.
     
      和标准的对比, @GenericGenerator是可用于包一级的注解,
     使之成为应用级的生成器(就象在JPA XML文件里面那样).

          @GenericGenerator(name="hibseq", strategy = "seqhilo",
        parameters = {
            @Parameter(name="max_lo", value = "5"),
            @Parameter(name="sequence", value="heybabyhey")
        }
    )
    package org.hibernate.test.model
     
     
     
         

         
           

      访问类型是根据 @Id或 @EmbeddedId
     在实体继承层次中所处的位置推演而得的.子实体(Sub-entities),
     内嵌对象和被映射的父类均继承了根实体(root entity)的访问类型.

      在Hibernate中,你可以把访问类型覆盖成:

           
             
                使用定制的访问类型策略
             

             
                优化类级或属性级的访问类型
             
           

            为支持这种行为,Hibernate引入了@AccessType注解.你可以对以下元素定义访问类型:

           
             
                实体
             

             
                父类
             

             
                可内嵌的对象
             

             
                属性
             
           

       被注解元素的访问类型会被覆盖,若覆盖是在类一级上,则所有的属性继承访问类型.
      对于根实体,其访问类型会被认为是整个继承层次中的缺省设置(可在类或属性一级覆盖).

       若访问类型被标以"property",则Hibernate会扫描getter方法的注解,若访问类型被标以"field",
      则扫描字段的注解.否则,扫描标为@Id或@embeddedId的元素.

       你可以覆盖某个属性(property)的访问类型,但是受注解的元素将不受影响:
       例如一个具有field访问类型的实体,(我们)可以将某个字段标注为 @AccessType("property"),
       则该字段的访问类型随之将成为property,但是其他字段上依然需要携带注解.

       若父类或可内嵌的对象没有被注解,则使用根实体的访问类型(即使已经在非直系父类或可内嵌对象上定义了访问类型).
       此时俄罗斯套娃(Russian doll)原理就不再适用.(译注:俄罗斯套娃(матрёшка或 матрешка)是俄罗斯特产木制玩具,
       一般由多个一样图案的空心木娃娃一个套一个组成,最多可达十多个,通常为圆柱形,底部平坦可以直立.)

    @Entity
    public class Person implements Serializable {
        @Id  @GeneratedValue //access type field
        Integer id;

        @Embedded
        @AttributeOverrides({
        @AttributeOverride(name = "iso2", column = @Column(name = "bornIso2")),
        @AttributeOverride(name = "name", column = @Column(name = "bornCountryName"))
                })
        Country bornIn;
    }

    @Embeddable
    @AccessType("property") //override access type for all properties in Country
    public class Country implements Serializable {
        private String iso2;
        private String name;

        public String getIso2() {
            return iso2;
        }

        public void setIso2(String iso2) {
            this.iso2 = iso2;
        }

        @Column(name = "countryName")
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

         

         
           

       有时候,你想让数据库,而非JVM,来替你完成一些计算,也可能想创建某种虚拟列.
      你可以使用SQL片段(亦称为公式),而不是将属性映射到(物理)列. 这种属性是只读的(属性值由公求得).
      
       @Formula("obj_length * obj_height * obj_width")
      public long getObjectVolume()
      
      
       SQL片段可以是任意复杂的,甚至可包含子查询.
         

         
           

            @org.hibernate.annotations.Type
      覆盖了Hibernate所用的默认类型:这通常不是必须的,因为类型可以由Hibernate正确推得.
      关于Hibernate类型的详细信息,请参考Hibernate使用手册.

            @org.hibernate.annotations.TypeDef 和
       @org.hibernate.annotations.TypeDefs允许你来声明类型定义.
      这些注解被置于类或包一级.注意,对session factory来说,
      这些定义将是全局的(即使定义于类一级),并且类型定义必须先于任何使用.

            @TypeDefs(
        {
        @TypeDef(
            name="caster",
            typeClass = CasterStringType.class,
            parameters = {
                @Parameter(name="cast", value="lower")
            }
        )
        }
    )
    package org.hibernate.test.annotations.entity;

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

         
           

            通过在列属性(property)上使用 @Index注解,
      可以在特定列上定义索引,columnNames属性(attribute)将随之被忽略.

            @Column(secondaryTable="Cat1")
    @Index(name="story1index")
    public String getStoryPart1() {
        return storyPart1;
    }
         

         
           

       在嵌入式对象内部,你可以在那些指向该嵌入式对象所属元素的属性上定义该注解.

            @Entity
    public class Person {
        @Embeddable public Address address;
        ...
    }

    @Embeddable
    public class Address {
        @Parent public Person owner;
        ...
    }


    person == person.address.owner
         

         
           

           
      某些属性可以在对数据库做插入或更新操作的时候生成.
      Hibernate能够处理这样的属性,并触发一个后续的查询来读取这些属性.
      

            @Entity
    public class Antenna {
        @Id public Integer id;
        @Generated(GenerationTime.ALWAYS) @Column(insertable = false, updatable = false)
        public String longitude;

        @Generated(GenerationTime.INSERT) @Column(insertable = false)
        public String latitude;
    }

           
      你可以将属性注解为 @Generated.
      但是你要注意insertability和updatability不要和你选择的生成策略冲突.
      如果选择了GenerationTime.INSERT,该属性不能包含insertable列,
      如果选择了GenerationTime.ALWAYS,该属性不能包含insertable和updatable列.

            @Version属性不可以为
            @Generated(INSERT)(设计时), 只能是
            NEVER或 ALWAYS.
         

       

       
         

          SINGLE_TABLE 是个功能强大的策略,但有时,特别对遗留系统而言,
      是无法加入一个额外的辨别符列.
      由此,Hibernate引入了辨别符公式(discriminator formula)的概念:
       @DiscriminatorFormula是 @DiscriminatorColumn的替代品,
      它使用SQL片段作为辨别符解决方案的公式( 不需要有一个专门的字段).

          @Entity
    @DiscriminatorForumla("case when forest_type is null then 0 else forest_type end")
    public class Forest { ... }

          默认情况下查询顶级实体,Hibernate不会加入带鉴别器列的约束条件子句.
          但是如果该列中还包含了和继承层次无关的值(通过 @DiscriminatorValue)
          就会很不方便.为了解决这个问题,你可以在类上使用 @ForceDiscriminator注解
          (将该注解放在 @DiscriminatorColumn后面).
          这样Hibernate在加载实体的时候就可以列出对应的值.
       

       
         

          默认情况下,当预期的被关联元素不在数据库中(关乎关联列的错误id),致使Hiberante无法解决关联性问题时,Hibernate就会抛出异常.
          这对遗留schema和历经拙劣维护的schema而言,这或有许多不便.
          此时,你可用 @NotFound 注解让Hibernate略过这样的元素而不是抛出异常.
          该注解可用于 @OneToOne (有外键)、 @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

         
           

           
      EJB3为延迟加载和获取模式提供了 fetch选项,而Hibernate
      这方面提供了更丰富的选项集.为了更好的调整延迟加载和获取策略,Hibernate引入了
      一些附加的注解:

           
             
                @LazyToOne: 定义了
                 @ManyToOne 和 @OneToOne
                关联的延迟选项. LazyToOneOption 可以是
                PROXY (例如:基于代理的延迟加载),
                NO_PROXY (例如:基于字节码增强的延迟加载 - 注意需要在构建期处理字节码)
                和 FALSE (非延迟加载的关联)
       

             
                @LazyCollection: 定义了
                @ManyToMany和
                @OneToMany  关联的延迟选项. LazyCollectionOption
                可以是 TRUE (集合具有延迟性,只有在访问的时候才加载),
        EXTRA (集合具有延迟性,并且所有的操作都会尽量避免加载集合,
       对于一个巨大的集合特别有用,因为这样的集合中的元素没有必要全部加载)和 FALSE
                (非延迟加载的关联)
             

             
        @Fetch:
       定义了加载关联关系的获取策略. FetchMode 可以是
                SELECT (在需要加载关联的时候触发select操作),
        SUBSELECT
                (只对集合有效,使用了子查询策略,详情参考Hibernate参考文档) or
        JOIN (在加载主实体(owner entity)的时候使用SQL JOIN来加载关联关系).
                JOIN 将覆写任何延迟属性
       (通过 JOIN策略加载的关联将不再具有延迟性).
             
           

            The Hibernate annotations overrides the EJB3 fetching
            options.

       Hibernate注解覆写了EJB3提供的获取(fetch)选项.

           

     


         


       

       
         

         
           

            以下是可能的设置方式
               
                   用@BatchSizebatch设置集合的batch大小
               

               
                   用@Where或@WhereJoinTable注解设置Where子句,
                   这两种注解分别应用于目标实体和关联表
               

               
                   用注解@Check来设置check子句
               

               
                   用注解@OrderBy来设置SQL的order by子句
               

               
                   利用@OnDelete(action=OnDeleteAction.CASCADE) 注解设置级连删除策略
               
             

            你也可以利用 @Sort注解定义一个排序比较器(sort comparator),
      表明希望的比较器类型,无序、自然顺序或自定义排序,三者择一.若你想用你自己实现的comparator,
      你还需要利用 comparator属性(attribute)指明实现类.
      注意你需要使用SortedSet或SortedMap接口
      

                @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
      

         

         
           
           
           
             

              比EJB3更胜一筹的是,Hibernate Annotations支持真正的
       List和Array.
       映射集合的方式和以前完全一样,只不过要新增 @IndexColumn注解.
       该注解允许你指明存放索引值的字段.你还可以定义代表数据库中首个元素的索引值(亦称为索引基数).
       常见取值为 0或 1.

              @OneToMany(cascade = CascadeType.ALL)
    @IndexColumn(name = "drawer_position", base=1)
    public List<Drawer> getDrawers() {
        return drawers;
    }

             
                假如你忘了设置 @IndexColumn,
       Hibernate会采用bag语义(译注:即允许重复元素的无序集合).
       如果你既想使用bag语义,但是又不希望受制于其约束语义,
       可以考虑使用 @CollectionId注解.
       
             
           

           
             
             
              Hibernate注解支持true Map映射,
              如果没有设置 @javax.persistence.MapKey,
              hibernate将key元素或嵌入式对象直接映射到他们所属的列.
              要覆写默认的列,可以使用以下注解:
              @org.hibernate.annotations.MapKey适用的key为基本类型
              (默认为 mapkey)或者嵌入式对象,
              @org.hibernate.annotations.MapKey适用的key为实体.
             

           
             
           
             

              双向关联的其中一端在使用 @IndexColumn或者
              @org.hibernate.annotations.MapKey[ManyToMany]注解
              需要考虑一些特殊的因素.如果子类存在某个属性映射到索引列,这种情况下是没有问题的,我们可以
              继续在集合映射的时候使用 mappedBy,如下:

              @Entity
    public class Parent {
        @OneToMany(mappedBy="parent")
        @org.hibernate.annotations.MapKey(columns=@Column(name="name"))
        private Map<String, Child> children;
        ...
    }

    @Entity
    public class Parent {
        ...
        @Basic
        private String name;

        @ManyToOne
        @JoinColumn(name="parent_id", nullable=false)
        private Parent parent;
        ...
    }

              但是,如果在子类中没有该属性,我们就不能认为这种关联是真正的双向关联
              (也就是在关联的一端有信息而另一端没有).因此在这种情况下,我们就不能使用
              mappedBy将其映射集合.取而代之为下面这种映射方式:

              @Entity
    public class Parent {
        @OneToMany
        @org.hibernate.annotations.MapKey(columns=@Column(name="name"))
        @JoinColumn(name="parent_id", nullable=false)
        private Map<String, Child> children;
        ...
    }

    @Entity
    public class Parent {
        ...
        @ManyToOne
        @JoinColumn(name="parent_id", insertable=false, updatable=false, nullable=false)
        private Parent parent;
        ...
    }

              注意在上面的映射中,关联的集合端负责更新外键.
           

           
             

              另外一个有趣的特征就是可以给bag集合定义一个代理主键.通过这种方式
              优雅的去除了bag的缺点:update和remove操作更加有效率,每次查询或每个实体可以
              超过一个 EAGER bag.该主键被保存在集合表的一个附加列,
              该列对于Java应用不可见.@CollectionId注解将一个集合标注为id bag,同时还
              可以覆写主键列,主键类型以及生成器策略.生成器策略可以是 identity,
              也可以是应用中任何已定义的生成器的名字.

              @Entity
    @TableGenerator(name="ids_generator", table="IDS")
    public class Passport {
        ...

        @ManyToMany(cascade = CascadeType.ALL)
        @JoinTable(name="PASSPORT_VISASTAMP")
        @CollectionId(
            columns = @Column(name="COLLECTION_ID"),
            type=@Type(type="long"),
            generator = "ids_generator"
        )
        private Collection<Stamp> visaStamp = new ArrayList();
        ...
    }
           

           
             

            Hibernate Annotations还支持核心类型集合(Integer, String, Enums, ......)、
      可内嵌对象的集合,甚至基本类型数组.这就是所谓的元素集合.

            元素集合可用@CollectionOfElements来注解(作为@OneToMany的替代).
      为了定义集合表(译注:即存放集合元素的表,与下面提到的主表对应),要在关联属性上使用@JoinTable注解,
      joinColumns定义了介乎实体主表与集合表之间的连接字段(inverseJoincolumn是无效的且其值应为空).
      对于核心类型的集合或基本类型数组,你可以在关联属性上用 @Column来覆盖存放元素值的字段的定义.
      你还可以用 @AttributeOverride来覆盖存放可内嵌对象的字段的定义.
      要访问集合元素,需要将该注解的name属性值设置为"element"("element"用于核心类型,而"element.serial"
      用于嵌入式对象的serial属性).要访问集合的index/key,则将该注解的name属性值设置为"key".
      

            @Entity
    public class Boy {
        private Integer id;
        private Set<String> nickNames = new HashSet<String>();
        private int[] favoriteNumbers;
        private Set<Toy> favoriteToys = new HashSet<Toy>();
        private Set<Character> characters = new HashSet<Character>();

        @Id @GeneratedValue
        public Integer getId() {
            return id;
        }

        @CollectionOfElements
        public Set<String> getNickNames() {
            return nickNames;
        }

        @CollectionOfElements
        @JoinTable(
                table=@Table(name="BoyFavoriteNumbers"),
                joinColumns = @JoinColumn(name="BoyId")
        )
        @Column(name="favoriteNumber", nullable=false)
        @IndexColumn(name="nbr_index")
        public int[] getFavoriteNumbers() {
            return favoriteNumbers;
        }

        @CollectionOfElements
        @AttributeOverride( name="element.serial", column=@Column(name="serial_nbr") )
        public Set<Toy> getFavoriteToys() {
            return favoriteToys;
        }

        @CollectionOfElements
        public Set<Character> getCharacters() {
            return characters;
        }
        ...
    }

    public enum Character {
        GENTLE,
        NORMAL,
        AGGRESSIVE,
        ATTENTIVE,
        VIOLENT,
        CRAFTY
    }

    @Embeddable
    public class Toy {
        public String name;
        public String serial;
        public Boy owner;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getSerial() {
            return serial;
        }

        public void setSerial(String serial) {
            this.serial = serial;
        }

        @Parent
        public Boy getOwner() {
            return owner;
        }

        public void setOwner(Boy owner) {
            this.owner = owner;
        }


        public boolean equals(Object o) {
            if ( this == o ) return true;
            if ( o == null || getClass() != o.getClass() ) return false;

            final Toy toy = (Toy) o;

            if ( !name.equals( toy.name ) ) return false;
            if ( !serial.equals( toy.serial ) ) return false;

            return true;
        }

        public int hashCode() {
            int result;
            result = name.hashCode();
            result = 29 * result + serial.hashCode();
            return result;
        }
    }

      
      在嵌入式对象的集合中,可以使用 @Parent注解嵌入式对象的某属性.
      该属性指向该嵌入式对象所属的集合实体.

           
        旧版的Hibernate Annotations用 @OneToMany来标识集合元素.
       由于语义矛盾,我们引入了 @CollectionOfElements注解.
       用 @OneToMany来标识集合元素的这种旧有方式目前尚有效,
       但是不推荐使用,而且在以后的发布版本中不再支持这种方式.
           
           
         
       

       
         

          为了优化数据库访问,你可以激活所谓的Hibernate二级缓存.该缓存是可以按每个实体和集合进行配置的.

          @org.hibernate.annotations.Cache定义了缓存策略及给定的二级缓存的范围.
      此注解适用于根实体(非子实体),还有集合.

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

              @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
        @JoinColumn(name="CUST_ID")
        @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
        public SortedSet<Ticket> getTickets() {
            return tickets;
        }

         

         
           
             

             

             
           

            @Cache(
        CacheConcurrencyStrategy usage();
        String region() default "";
        String include() default "all";
    )

           
             
                usage: 给定缓存的并发策略(NONE,
                READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL)
             

             
                region (可选的):缓存范围(默认为类的全限定类名或是集合的全限定角色名)
             

             
                include (可选的):值为all时包括了所有的属性(proterty),
                为non-lazy时仅含非延迟属性(默认值为all)
             
           

         
       

       
         

          Hibernate具有在数据上应用任意过滤器的能力,可在运行期应用于一个给定的session.过滤器需要事先定义好.

          @org.hibernate.annotations.FilterDef 或
          @FilterDefs 定义过滤器声明,为同名过滤器所用.
          过滤器声明带有一个name()和一个parameters()数组. 参数提供了在运行时调整
          过滤器行为的能力,过滤器通过 @ParamDef注解定义,该注解包含name和type,
          你还可以为给定的 @FilterDef定义一个defaultCondition()参数,
          当所有的 @Filter中没有任何定义时,可使用该参数定义缺省条件.
         @FilterDef (s)可以在类或包一级进行定义.

          现在我们来定义应用于实体或集合加载时的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引入了
          @org.hibernate.annotations.NamedQuery,
          @org.hibernate.annotations.NamedQueries,
          @org.hibernate.annotations.NamedNativeQuery 和
          @org.hibernate.annotations.NamedNativeQueries 命名式查询,
          因此Hibernate在命名式查询上比EBJ3规范中所定义的命名式查询提供了更多的特性.
          他们在标准版中添加了可作为替代品的一些属性(attributes):

         
           
              flushMode: 定义查询的刷新模式(Always, Auto, Commit或Never)
           

           
              cacheable: 查询该不该被缓存
           

           
              cacheRegion: 若查询已被缓存时所用缓存的范围
           

           
              fetchSize: 针对该查询的JDBC statement单次获取记录的数目
           

           
              timeout: 查询超时
           

           
              callable: 仅用于本地化查询(native query),对于存储过程设为true
           

           
              comment: 一旦激活注释功能,我们会在向数据库交送查询请求时看到注释
           

           
              cacheMode: 缓存交护模式(get, ignore,normal,或refresh)
           

           
              readOnly: 不管是否从查询获取元素都是在只读模式下
           
         

          通过 @QueryHint注解可以在
          @javax.persistence.NamedQuery
          中设置hints.另一个重要的优势是可以将这些注解应用到包上.
         
     

  • 发表评论