hibernate注解(三)

原创

hibernate注解(三)

我们继续介绍hibernate注解的相关内容

1. OneToOne懒加载问题

一对一注解时,若采用外键列进行实体的关联的话,懒加载问题是需要注意下的。如下:

Student表:

id  int  not null
name  varchar(50)  not null
card_id  int  not null

Card表:

id  int  not null
card_no  varchar()  not null

Student类


 
 
  1. public class Student {
  2. private int id;
  3. private String name;
  4. private Card card;
  5. ....
  6. @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  7. @JoinColumn(name = "card_id")
  8. public Card getCard() {
  9. return card;
  10. }
  11. public void setCard(Card card) {
  12. this.card = card;
  13. }
  14. }

Card类


 
 
  1. public class Card {
  2. private int id;
  3. private String cardNo;
  4. private Student student;
  5. ....
  6. @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "card")
  7. public Student getStudent() {
  8. return student;
  9. }
  10. public void setStudent(Student student) {
  11. this.student = student;
  12. }
  13. }

 按照上述关系,运行代码后我们会发现,Student获取Card时可以懒加载,但Card获取Student时却无法懒加载,那么这是为什么呢?

原因:在Card表里由于没有关系字段,因此仅从Card表角度看无法知道拥有该卡的学生是谁(除非在Card表建立Student表的外键student_id,但由于冗余了关系字段,因此很少有人这么干吧【注意sql的join语句是考虑了两张表的】),而从Student角度不一样,由于含有card_id字段可能清楚知道该学生拥有一张卡。

正是由于上面的原因,因此当从Card获取Student时,hibernate为了确定Student表中到底有没有该Card,因此发了一条sql:select * from Student where card_id = ?,参数既是该Card的id,以此来维护Card与Student的关系。

还有一种解释,但需要理解hibernate的懒加载的机制:代理。

2. 懒加载原理

hibernate使用了代理(Proxy),对实体的调用会被代理接受和处理,hibernate可以设置这个代理被调用到的时候去加载数据,从而实现延迟加载。那么对于一个映射对象,要么它有值,要么它是null,对于null值建立代理是没多大作用的,而且也不能对null建立动态代理。那就是说hibernate在对延迟加载建立代理的时候要考虑这个映射的对象是否是null。如果是null不需要建立代理,直接把映射的值设置成null,如果映射的对象不为null,那么hibernate就建立代理对象。

简而言之,为null就不能懒加载,为代理对象才能懒加载。

 

那么解决上述问题的办法呢?

● Card类的getStudent改为manyToOne,并设置unique=true。缺点:需要将字段改为Set<Student>。

● 手动为Student建立代理对象。(不建议这么做,而且我也没试过)

● 采用no-proxy懒加载机制,对类用instrument进行增强,即用类增强器对二进制Class文件进行强化处理。(很少有人这么做,不推荐)

以下是利用ant调用hibernate类增强器对class文件进行强化处理,ant的build.xml脚本如下:


 
 
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project name="hibernatelazy" default="instrument" basedir=".">
  3. <property name="lib.dir" value="./lib"/>
  4. <property name="classes.dir" value="./classes"/>
  5. <path id="lib.class.path">
  6. <fileset dir="${lib.dir}">
  7. <include name="**/*.jar"/>
  8. </fileset>
  9. </path>
  10. <target name="instrument">
  11. <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
  12. <classpath path="${classes.dir}"/>
  13. <classpath refid="lib.class.path"/>
  14. </taskdef>
  15. <instrument verbose="true">
  16. <fileset dir="${classes.dir}/com/derek/known/hbm">
  17. <include name="Knownquestions.class"/>
  18. </fileset>
  19. </instrument>
  20. </target>
  21. </project>

其中注意:

<property name="lib.dir" value="./lib"/>所需的JAR文件路径。

<property name="classes.dir" value="./classes"/>编译输出路径。

我把build.xml放在了WEB-INF目录下,输出路径就设置为该目录下的classes目录,待增强的字节码文件为classes目录下的com/derek/known/hbm/Knownquestions.class; 在命令行下切换到此目录,执行ant命令,即生成新的Knownquestions.class。

● 放弃懒加载,改为join的加载策略,或者使用hql:from Card t inner join fetch t.student r

3. 懒加载注解方式详解

● FetchType(@OneToOne、@OneToMany等注解里的fetch属性对应的类,本身没有注解)

JPA标准的通用加载策略注解属性。FetchType可选值意义与区别如下:

FetchType.LAZY:懒加载,在第一次访问关联对象的时候加载

FetchType.EAGER:立刻加载,在查询主对象的时候同时加载关联对象。

● @Fetch

hibernate定义了加载关联关系的获取策略。Fetch可选值意义与区别如下:

FetchMode.JOIN:始终立刻加载,使用外连(outer join)查询的同时加载关联对象,忽略FetchType.LAZY设定。

FetchMode.SELECT:支持懒加载(除非设定关联属性lazy=false),当访问每一个关联对象时加载该对象,会累计产生N+1条sql语句

FetchMode.SUBSELECT:支持懒加载(除非设定关联属性lazy=false),在访问第一个关联对象时加载所有的关联对象。会累计产生两条sql语句。

● @LazyToOne

hibernate定义了@ManyToOne 和@OneToOne 关联的延迟选项。LazyToOne可选值意义与区别如下:

LazyToOneOption.PROXY:基于代理的延迟加载。

LazyToOneOption.NO_PROXY:基于字节码增强的延迟加载 - 注意需要在构建期处理字节码增强。

LazyToOneOption.FALSE:非延迟加载的关联。

● @LazyCollection

hibernate定义了@ManyToMany和@OneToMany 关联的延迟选项。LazyCollection可选值意义与区别如下:

LazyCollectionOption.TRUE:集合具有延迟性,只有在访问的时候才加载。

LazyCollectionOption.EXTRA:集合具有延迟性,并且所有的操作都会尽量避免加载集合, 对于一个巨大的集合特别有用,因为这样的集合中的元素没有必要全部加载。

LazyCollectionOption.FALSE:非延迟加载的关联。

 

延迟和获取选项的等效注解

Annotations

Lazy

Fetch

@[One|Many]ToOne](fetch=FetchType.LAZY)

@LazyToOne(PROXY)

@Fetch(SELECT)

@[One|Many]ToOne](fetch=FetchType.EAGER)

@LazyToOne(FALSE)

@Fetch(JOIN)

@ManyTo[One|Many](fetch=FetchType.LAZY)

@LazyCollection(TRUE)

@Fetch(SELECT)

@ManyTo[One|Many](fetch=FetchType.EAGER)

@LazyCollection(FALSE)

@Fetch(JOIN)

文章最后发布于: 2013-05-31 14:16:54
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值