EJB3.0注解


2011-06-01 01:26

EJB3.0注解

本章内容覆盖了EJB3.0(也就是JPA)实体的注解规范以及Hibernate特有的扩展.    

 

现在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;

    @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 = ", "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上进行了小小的提升,

   允许在同一实体中使用两个同类型的嵌入对象而无须额外的声明.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值