hibernate annotations.docx
- 文档编号:18125307
- 上传时间:2023-08-13
- 格式:DOCX
- 页数:28
- 大小:801.56KB
hibernate annotations.docx
《hibernate annotations.docx》由会员分享,可在线阅读,更多相关《hibernate annotations.docx(28页珍藏版)》请在冰点文库上搜索。
hibernateannotations
第7章HibernateAnnotations
本章学习目标
熟练掌握使用HibernateAnnotations的使用
进一步掌握Hibernate的使用
本章技能目标
使用HibernateAnnotation进行开发
本章简介
近来,越来越多的软件开发公司开始使用Annotations替代xml配置进行DAO开发,因为Annotations 使用起来比较简单,所以很受程序员的喜欢,本章将详细向大家讲解如何使用Annotations进行相关应用的开发。
7.1. 系统需求
首先从Hibernate官方网站下载并解压HibernateAnnotations的发布包。
这个版本(预览版)要求使用Hibernate3.2.0.CR2或更高版本。
请不要和老版本的Hibernate3.x混合起来使用。
这个版本在Hibernatecore3.2.0.CR2的基础上工作良好。
首先确定你已经安装了JDK5.0。
当然就算使用低版本的JDK,Xdoclet也可以提供(基于注解的)元数据所带来的部分功能。
7.1.2. 系统配置
首先就是设置classpath(当然是在IDE中创建了一个新项目之后)。
将Hibernate3核心文件以及其依赖的第三方库文件(请参考lib/README.txt文件)加入到你的classpath里面。
将hibernate-annotations.jar和lib/ejb3-persistence.jar加入到你的classpath里面。
我们推荐在一个包装器(wrapper)类HibernateUtil的静态初始化代码块中启动Hibernate。
或许你在Hibernate文档的其他很多地方看到过这个类,但是要在你的项目中使用注解,还需要对这个辅助(helper)类进行扩展。
扩展如下:
这里比较有意思的是使用到了AnnotationConfiguration类。
在XML配置文件(通常是hibernate.cfg.xml)中则定义了包和经过注解的类。
下面的xml和前面的声明等价:
注意现在你可以混合使用hbm.xml和注解。
资源元素(resourceelement)可以是hbm文件也可以是EJB3XML发布描述符,此差别对于配置过程是透明的。
除了上面的方式,你还可以通过编程的方式定义包括注解的类和包
你也可以使用HibernateEntityManager来完成以上功能。
除了启动方式和配置文件有所改变之外,结合注解来使用HibernateAPI和以前没有什么区别,在其他方面你还是可以继续保持以前的习惯和喜好(hibernate.properties,hibernate.cfg.xml,programmaticAPIs等等)。
甚至对于同一个SessionFactory,你都可以混合带注解的持久类以及传统的bm.cfg.xml声明方式。
然而你不能多次声明同一个类(要么通过注解要么通过hbm.xml配置文件),而且在一个映射实体的类继承层次中,这两个配置策略不能同时使用.
为了简化从hbm文件到注解的迁移过程,配置机制将自动检测在注解和hbm文件中重复的映射。
默认情况下hbm文件中的声明比类中的注解元数据具有更高的优先级。
这种优先级的设定是以类为单位的。
你也可以通过hibernate.mapping.precedence修改这种优先级。
默认的值是hbm,class,如果改为class,hbm,当发生冲突的时候,类中的注解将比hbm文件具有更高的优先级。
7.2 实体Bean
7.2.1简介
本节内容覆盖了EJB3.0实体bean的注解规范以及Hibernate特有的扩展.
7.2.2用EJB3注解进行映射
现在EJB3实体Bean是纯粹的POJO.实际上这表达了和Hibernate持久化实体对象同样的概念.它们的映射都通过JDK5.0注解来定义(EJB3规范中的XML描述语法至今还没有最终定下来).注解分为两个部分,分别是逻辑映射注解和物理映射注解,通过逻辑映射注解可以描述对象模型,类之间的关系等等,而物理映射注解则描述了物理的schema,表,列,索引等等.下面我们在代码中将混合使用这两种类型的注解.EJB3注解的API定义在javax.persistence.*包里面.大部分和JDK5兼容的IDE(象Eclipse,IntelliJIDEA和Netbeans等等)都提供了注解接口和属性的自动完成功能.(这些不需要IDE提供特别的EJB3支持模块,因为EJB3注解是标准的JDK5注解)HibernateAnnotations提供的大部分单元测试代码都演示了实际的例子,是一个获取灵感的好地方.
7.2.3声明实体bean
每一个持久化POJO类都是一个实体bean,这可以通过在类的定义中使用@Entity注解来进行声明:
通过@Entity注解将一个类声明为一个实体bean(即一个持久化POJO类),@Id注解则声明了该实体bean的标识属性.其他的映射定义是隐式的.这种以隐式映射为主体,以显式映射为例外的配置方式在新的EJ3规范中处于非常重要的位置,和以前的版本相比有了质的飞跃.在上面这段代码中:
Flight类映射到Flight表,并使用id列作为主键列.
在对一个类进行注解时,你可以选择对它的的属性或者方法进行注解,根据你的选择,Hibernate的访问类型分别为field或property.EJ3规范要求在需要访问的元素上进行注解声明,例如,如果访问类型为property就要在getter方法上进行注解声明,如果访问类型为field就要在字段上进行注解声明.应该尽量避免混合使用这两种访问类型.Hibernate根据@Id或@EmbeddedId的位置来判断访问类型.
7.2.4定义表(Table)
@Table是类一级的注解,通过@Table注解可以为实体bean映射指定表(table),目录(catalog)和schema的名字.如果没有定义@Table,那么系统自动使用默认值:
实体的短类名(不附带包名).
@Table元素包括了一个schema和一个catalog属性,如果需要可以指定相应的值.结合使用@UniqueConstraint注解可以定义表的唯一约束(uniqueconstraint)(对于绑定到单列的唯一约束,请参考@Column注解)
上面这个例子中,在month和day这两个字段上定义唯一约束.注意columnNames数组中的值指的是逻辑列名.
Hibernate在NamingStrategy的实现中定义了逻辑列名.默认的EJB3命名策略将物理字段名当作逻辑字段名来使用.注意该字段名和它对应的属性名可能不同(如果字段名是显式指定的话).除非你重写了NamingStrategy,否则不用担心这些区别..
7.2.5乐观锁定版本控制
你可以在实体bean中使用@Version注解,通过这种方式可添加对乐观锁定的支持:
上面这个例子中,version属性将映射到OPTLOCK列,entitymanager使用该字段来检测更新冲突(防止更新丢失,请参考last-commit-wins策略).
根据EJB3规范,version列可以是numeric类型(推荐方式)也可以是timestamp类型.Hibernate支持任何自定义类型,只要该类型实现了UserVersionType.
7.3映射简单属性
7.3.1声明基本的属性映射
实体bean中所有的非static非transient的属性都可以被持久化,除非你将其注解为@Transient.所有没有定义注解的属性等价于在其上面添加了@Basic注解.通过@Basic注解可以声明属性的获取策略(fetchstrategy):
上面这个例子中,counter是一个transient的字段,lengthInMeter的getter方法被注解为@Transient,entitymanager将忽略这些字段和属性.而name,length,firstname这几个属性则是被定义为可持久化和可获取的.对于简单属性来说,默认的获取方式是即时获取(earlyfetch).当一个实体Bean的实例被创建时,Hibernate会将这些属性的值从数据库中提取出来,保存到Bean的属性里.与即时获取相对应的是延迟获取(lazyfetch).如果一个属性的获取方式是延迟获取(比如上面例子中的detailedComment属性),Hibernate在创建一个实体Bean的实例时,不会即时将这个属性的值从数据库中读出.只有在该实体Bean的这个属性第一次被调用时,Hibernate才会去获取对应的值.通常你不需要对简单属性设置延迟获取(lazysimpleproperty),千万不要和延迟关联获取(lazyassociationfetch)混淆了。
Hibernate和EJB3都支持所有基本类型的属性映射.这些基本类型包括所有的Java基本类型,及其各自的wrapper类和serializable类.HibernateAnnotations还支持将内置的枚举类型映射到一个顺序列(保存了相应的序列值)或一个字符串类型的列(保存相应的字符串).默认是保存枚举的序列值,但是你可以通过@Enumerated注解来进行调整(见上面例子中的note属性)。
在核心的JavaAPI中并没有定义时间精度(temporalprecision).因此处理时间类型数据时,你还需要定义将其存储在数据库中所预期的精度.在数据库中,表示时间类型的数据有DATE,TIME,和TIMESTAMP三种精度(即单纯的日期,时间,或者两者兼备).可使用@Temporal注解来调整精度。
@Lob注解表示属性将被持久化为Blob或者Clob类型,具体取决于属性的类型,java.sql.Clob,Character[],char[]和java.lang.String这些类型的属性都被持久化为Clob类型,而java.sql.Blob,Byte[],byte[]和serializable类型则被持久化为Blob类型.
如果某个属性实现了java.io.Serializable同时也不是基本类型,并且没有在该属性上使用@Lob注解,那么Hibernate将使用自带的serializable类型。
7.3.2 声明列属性
在上面这个例子中,name属性映射到flight_name列.该字段不允许为空,长度为50,并且是不可更新的(也就是属性值是不变的)。
上面这些注解可以被应用到正规属性上例如@Id或@Version属性.
(1)
name可选,列名(默认值是属性名)
(2)
unique可选,是否在该列上设置唯一约束(默认值false)
(3)
nullable可选,是否设置该列的值可以为空(默认值false)
(4)
insertable可选,该列是否作为生成的insert语句中的一个列(默认值true)
(5)
updatable可选,该列是否作为生成的update语句中的一个列(默认值true)
(6)
columnDefinition可选:
为这个特定列覆盖SQLDDL片段(这可能导致无法在不同数据库间移植)
(7)
table可选,定义对应的表(默认为主表)
(8)
length可选,列长度(默认值255)
(8)
precision可选,列十进制精度(decimalprecision)(默认值0)
(10)
scale可选,如果列十进制数值范围(decimalscale)可用,在此设置(默认值0)
7.3.3 映射主键属性
使用@Id注解可以将实体bean中的某个属性定义为标识符(identifier).该属性的值可以通过应用自身进行设置,也可以通过Hiberante生成(推荐).使用@GeneratedValue注解可以定义该标识符的生成策略:
1.AUTO-可以是identitycolumn类型,或者sequence类型或者table类型,取决于不同的底层数据库.
2.TABLE-使用表保存id值
3.IDENTITY-identitycolumn
4.SEQUENCE-sequence
和EJB3规范相比,Hibernate提供了更多的id生成器。
下面是定义组合主键的几种语法:
1.将组件类注解为@Embeddable,并将组件的属性注解为@Id
2.将组件的属性注解为@EmbeddedId
3.将类注解为@IdClass,并将该实体中所有属于主键的属性都注解为@Id
对于EJB2的开发人员来说@IdClass是很常见的,但是对于Hibernate的用户来说就是一个崭新的用法.组合主键类对应了一个实体类中的多个字段或属性,而且主键类中用于定义主键的字段或属性和实体类中对应的字段或属性在类型上必须一致.下面我们看一个例子:
//partoftheidkey
@IdpublicStringgetLastname(){
returnlastname;
}
publicvoidsetLastname(Stringlastname){
this.lastname=lastname;
}
publicStringgetClub(){
returnclub;
}
publicvoidsetClub(Stringclub){
this.club=club;
}
//appropriateequals()andhashCode()implementation
}
@Embeddable
publicclassFootballerPkimplementsSerializable{
//samenameandtypeasinFootballer
publicStringgetFirstname(){
returnfirstname;
}
publicvoidsetFirstname(Stringfirstname){
如上,@IdClass指向对应的主键类.
Hibernate支持在组合标识符中定义关联(就像使用普通的注解一样),而EJB3规范并不支持此类用法.
@Entity
@AssociationOverride(name="id.channel",joinColumns=@JoinColumn(name="chan_id"))
publicclassTvMagazin{
@EmbeddedIdpublicTvMagazinPkid;
@Temporal(TemporalType.TIME)Datetime;
}
7.3.4 每个类一张表
这种策略有很多缺点(例如:
多态查询和关联),EJB3规范,Hibernate参考手册,HibernateinAction,以及其他许多地方都对此进行了描述和解释.Hibernate使用SQLUNION查询来实现这种策略.通常使用场合是在一个继承层次结构的顶端:
这种策略支持双向的一对多关联.这里不支持IDENTITY生成器策略,因为id必须在多个表间共享.当然,一旦使用这种策略就意味着你不能使用AUTO生成器和IDENTITY生成器.
7.4 映射实体Bean的关联关系
7.4.1 一对一(One-to-one)
使用@OneToOne注解可以建立实体bean之间的一对一的关联.一对一关联有三种情况:
一是关联的实体都共享同样的主键,二是其中一个实体通过外键关联到另一个实体的主键(注意要模拟一对一关联必须在外键列上添加唯一约束).三是通过关联表来保存两个实体之间的连接关系(注意要模拟一对一关联必须在每一个外键上添加唯一约束).
首先,我们通过共享主键来进行一对一关联映射:
上面的例子通过使用注解@PrimaryKeyJoinColumn定义了一对一关联.
下面这个例子使用外键列进行实体的关联.
上面这个例子中,Customer通过Customer表中名为的passport_fk外键列和Passport关联.@JoinColumn注解定义了联接列(joincolumn).该注解和@Column注解有点类似,但是多了一个名为referencedColumnName的参数.该参数定义了所关联目标实体中的联接列.注意,当referencedColumnName关联到非主键列的时候,关联的目标类必须实现Serializable,还要注意的是所映射的属性对应单个列(否则映射无效).
一对一关联可能是双向的.在双向关联中,有且仅有一端是作为主体(owner)端存在的:
主体端负责维护联接列(即更新).对于不需要维护这种关系的从表则通过mappedBy属性进行声明.mappedBy的值指向主体的关联属性.在上面这个例子中,mappedBy的值为passport.最后,不必也不能再在被关联端(ownedside)定义联接列了,因为已经在主体端进行了声明.
如果在主体没有声明@JoinColumn,系统自动进行处理:
在主表(ownertable)中将创建联接列,列名为:
主体的关联属性名+下划线+被关联端的主键列名.在上面这个例子中是passport_id,因为Customer中关联属性名为passport,Passport的主键是id.
Thethirdpossibility(usinganassociationtable)isveryexotic.
第三种方式也许是最另类的(通过关联表).
Customer通过名为CustomerPassports的关联表和Passport关联;该关联表拥有名为passport_fk的外键列,该外键指向Passport表,该信息定义为inverseJoinColumn的属性值,而customer_fk外键列指向Customer表,该信息定义为joinColumns的属性值.
这种关联可能是双向的.在双向关联中,有且仅有一端是作为主体端存在的:
主体端负责维护联接列(即更新).对于不需要维护这种关系的从表则通过mappedBy属性进行声明.mappedBy的值指向主体的关联属性.在上面这个例子中,mappedBy的值为passport.最后,不必也不能再在被关联端(ownedside)定义联接列了,因为已经在主体端进行了声明.
你必须明确定义关联表名和关联列名.
7.4.2 多对一(Many-to-one)
在实体属性一级使用@ManyToOne注解来定义多对一关联:
其中@JoinColumn是可选的,关联字段默认值和一对一(onetoone)关联的情况相似,列名为:
主体的关联属性名+下划线+被关联端的主键列名.在这个例子中是company_id,因为关联的属性是company,Company的主键是id.
@ManyToOne注解有一个名为targetEntity的参数,该参数定义了目标实体名.通常不需要定义该参数,因为在大部分情况下默认值(表示关联关系的属性类型)就可以很好的满足要求了.不过下面这种情况下这个参数就显得有意义了:
使用接口作为返回值而不是常见的实体.
对于多对一也可以通过关联表的方式来映射。
通过@JoinTable注解可定义关联表,该关联表包含了指回实体表的外键(通过@JoinTable.joinColumns)以及指向目标实体表的外键(通过@JoinTable.inverseJoinColumns).
7.4.3 一对多(One-to-many)
在属性级使用@OneToMany注解可定义一对多关联.一对多关联可以是双向关联.
7.4.4 双向(Bidirectional)
在EJB3规范中多对一这端几乎总是双向关联中的主体(owner)端,而一对多这端的关联注解为@OneToMany(mappedBy=...)
Troop通过troop属性和Soldier建立了一对多的双向关联.在mappedBy端不必也不能再定义任何物理映射
对于一对多的双向映射,如果要一对多这一端维护关联关系,你需要删除mappedBy元素并将多对一这端的@JoinColumn的insertable和updatable设置为false.很明显,这种方案不会得到什么明显的优化,而且还会增加一些附加的UPDATE语句.
7.4.5 单向(Unidirectional)
通过在被拥有的实体端(ownedentity)增加一个外键列来实现一对多单向关联是很少见的,也是不推荐的.我们强烈建议通过一个联接表(jointable)来实现这种关联(下一节会对此进行解释).可以通过@JoinColumn注解来描述这种单向关联关系.
Customer通过CUST_ID列和Ticket建立了单向关联关系.
7.4.5 通过关联表处理单向关联
通过联接表处理单向一对多关联是首选方式.这种关联通过@JoinTable注解来进行描述.
上面这个例子中,Trainer通过TrainedMonkeys表和Monkey建立了单向关联.其中外键trainer_id关联到Trainer(joinColumns),而外键monkey_id关联到Monkey(inversejoinColumns).
7.4.6 多对多(Many-to-many)
7.4.6.1 定义
你可以通过@ManyToMany注解可定义的多对多关联.同时,你也需要通过注解@JoinTable描述关联表和关联条件.如果是双向关联,其中一段必须定义为owner,另一端必须定义为inverse(在对关联表进行更新操作时这一端将被忽略):
至此,我们已经展示了很多跟关联有关的声明定义以及属性细节.下面我们将深入介绍@JoinTable注解,该注解定义了联接表的表名,联接列数组(注解中定义数组的格式为{A,B,C}),以及inverse联接列数组.后者是关联表中关联到Employee主键的列(the"otherside").
正如前面所示,被关联端不必也不能描述物理映射:
只需要一个简单的mappedBy参数,该参数包含了主体端的属性名,这样就绑定双方的关系.
7.4.6.2 默认值
和其他许多注解一样,在多对多关联中很多值是自动生成.当双向多对多关联中没有定义任何物理映射时,Hibernate根据以下规则生成相应的值.关联表名:
主表表名+_下划线+从表表名,关联到主表的外键名:
主表名+_下划线+主表中的主键列名.关联到从表的外键名:
主表中用于关联的属性名+_下划线+从表的主键列名.以上规则对于双向一对多关联同样有效.
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- hibernate annotations