hibernate注解(三)

来源:互联网 发布:电子书包软件 编辑:程序博客网 时间:2024/05/16 08:42

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

1. OneToOne懒加载问题

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

Student表:

id  int  not nullname  varchar(50)  not nullcard_id  int  not null

Card表:

id  int  not nullcard_no  varchar()  not null

Student类

public class Student {private int id;private String name;private Card card;....@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)@JoinColumn(name = "card_id")public Card getCard() {return card;}public void setCard(Card card) {this.card = card;}}

Card类

public class Card {private int id;private String cardNo;private Student student;....@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "card")public Student getStudent() {return student;}public void setStudent(Student student) {this.student = student;}}

 按照上述关系,运行代码后我们会发现,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脚本如下:

<?xml version="1.0" encoding="UTF-8"?><project name="hibernatelazy" default="instrument" basedir=".">       <property name="lib.dir" value="./lib"/>       <property name="classes.dir" value="./classes"/>       <path id="lib.class.path">             <fileset dir="${lib.dir}">                    <include name="**/*.jar"/>              </fileset>        </path>        <target name="instrument">            <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">                     <classpath path="${classes.dir}"/>                     <classpath refid="lib.class.path"/>             </taskdef>             <instrument verbose="true">                      <fileset dir="${classes.dir}/com/derek/known/hbm">                            <include name="Knownquestions.class"/>                      </fileset>              </instrument>         </target></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)

原创粉丝点击