Hibernate 延迟加载(懒加载)与抓取策略

 

延迟加载与抓取策略

延迟加载,也叫懒加载。它是Hibernate为提高程序执行效率而提供的一种机制,即只有真正使用该对象的数据时才会创建。

Hibernate中主要是通过代理(proxy)机制来实现延迟加载的。它的具体过程是:Hibernate从数据库获取某一个对象数据时、获取某一个对象的集合属性值时,或获取一个对象所关联的另一个对象时,由于没有使用该对象的数据,Hibernate并不从数据库加载真正的数据,而只是为该对象创建一个代理对象来代表这个对象,真正从数据库中加载它的数据。这样在某些情况下,就可以提高查询效率。

有如下程序代码

Account acc=(Account)session

.load(Account.class,new Long(1));//返回的是一个代理对象

System.out.println(acc.getId());//没有发SQL语句到数据库加载数据

//创建真是的Account实例,并发送SQL语句到数据库中加载数据

System.out.println(acc.getLoginName());

Session的load()方法对实体的加载默认采用延迟加载(注:get()方法默认采用立即加载),所以以上第一行代码只返回一个代理对象,而第三行代码Hibernate才创建真是的Account实例,并发送SQL语句到数据库中加载数据。如果只访问对象标示符属性,它就没必要初始化代理。

在只需要一个Account类的一个引用时,这种延迟加载就很有用;例如:

Account acc=(Account)session.load(Account.class,new Long(1));

Order order=new order();

order.setCreatedTime(new Date());

order.setAccount(acc);

session.save(order);

在以上代码的第一行,它只是为Account类返回了一个代理对象,并没哟执行数据库查询。在这里也只需要Account的实例来创建一个新的Order(订单)对象,当调用session.save(order)时,将执行insert SQL语句,它也只需要Account的主标示符值作为外键报讯到订单表的对应字段中,这样就少执行一个数据库的select语句了,从而提高了查询效率。

       Hibernate中默认采用延迟加载的情况主要有以下几种:

1、  当调用session上的load()方法加载一个实体时,会采用延迟加载。

2、  当Session加载某个实体时,会对这个实体中的集合属性值采用延迟加载。

3、  当Session加载某个实体时,会对这个实体所单端关联(one-to-one,many-to-one)的另一个实体对象采用延迟加载。

关闭延迟加载

       延迟加载确实会给程序的查询效率带来好处,但有时明确知道数据需要立即加载的。如果Hibernate先默认使用延迟加载,而后又必须去数据库加载,反而会降低效率。所以,需要根据应用程序的实际情况来灵活控制是否使用延迟加载。在Hibernate中只需要修改相应的配置来启用或关闭延迟加载功能。

       在加载单个实体,如果不需要延迟加载,就可以使用Session的get()方法。

       当Session加载某个实体时,不需要对这个实体中的集合属性值延迟加载,而是要立即加载。这时可以在映射文件中针对这个集合的配置元素(<set>、<bag>、<list>……)添加属性lazy=false。

       当Session加载某个实体时,不需要对这个实体所单端关联的另一个实体对象延迟加载,就可以在映射文件中针对这个单端关联的配置元素(<one-to-one>、<many-to-one>)添加属性lazy=false。

大对象的延迟加载

       另外由于Hibernate针对实体类的普通属性(除标示符属性、集合属性和单端关联属性)默认都是采用立即加载,但在实体的某个属性类型为大对象(CLOB,BLOB)时,可能经常需要对这个属性进行延迟加载,此时可以在映射文件中针对这个属性的配置元素上添加属性lazy=true。

<?xml version=”1.0” encoding=”utf-8”?>

<!DOCTYPE hibernate-mapping PUBLIC

       “-//Hibernate/Hibernate Mapping DTD 3.0//EN”

       http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd>

<hibernate-mapping>

       <!--  映射持久化类 --  >

       <class name=”com.qiujy.domian.Article” table=”article”>

              <!--  映射对象标示符 --  >

              <id name=”id” column=”id” ><generator class=”native”/></id>

              <!--  映射普通属性 --  >

              <property name=”title”/>

              <property name=”author”/>

              <!--  对大对象类型的属性进行延迟加载.需要类增强器对本类的字节码文件进行强化处理,详见ant脚本文件build.xml --  >

              <property name=”content” type=”clob” lazy=”true”>

                     <column name=”content” type=”longtext”/>

              </property>

              <property name=”createdtime” column=”created_time” type=”timestamp”/>

</class>

</hibernate-mapping>

另外还需要使用类增强器对二进制Xlass文件进行强化处理,Hibernate3中针对英语那那个程序中所使用的动态代理类库(cglib或javassist)提供对应的增强工具类,可以借助ANT来定义一个任务队需要进行强化处理的字节码文件进行强化处理。ANT脚本build.xml文件如下:

<?xml version=”1.0” encoding=”UTF-8”?>

<project basedir=”.” Default=”build” name=”hb_09_lazy”>

       <!--  指定第三方类库的存放位置 --  >

       <property name=”lib.dir” value=”./3rdlibs”/>

       <!--  指定编译输出的目的地 --  >

       <property name=”class.dir” value=”./bin”/>

       <!--  指定本应用所依赖的第三类库 --  >

       <path id=”project.classpath”>

              <fileset dir=”${lib.dir}”>

                     <include name=”**/*.jar”/>

              </fileset>

</path>

<!--  类增强工具ant任务 --  >

<target name=”instrument”>

       <!--  根据应用程序锁使用的而动态代理类库(cglib或javassist),选择对应的字节码增强工具类 --  >

       <taskdef name=”instrument”

              classname=”org.hibernate.tool.instrument.javassist.InstrumentTask”

                     classpathref=”project.classpath”/>

       <!--  指定要进行强化处理的类文件 -- >

       <instrument verbose=”true”>

              <fileset dir=”${classes.dir}/com.qiujy/domain”>

                     <include name=”Article.class”/>

              </fileset>

       </instrument>

</target>

<!--  默认的任务bulid --  >

<target name=”build” depends=”instrument”/>

</project>

执行这个ANThou任务后,就可以对指定的字节码文件Article.class进行强化处理,之后就可以针对它的大对象属性content进行延迟加载。

抓取策略

通过抓取策略来直接影响Session()和get()或load()方法的查询效率。主要有以下两种情况。

1.       单端关联(<many-to-one>、<one-to-one>)上的抓取策略

可以给单端关联的映射元素添加fetch属性。Fetch属性有两个可选值。

Select:作为默认值,它的策略是当需要使用到关联对象的数据时,两位单独发送一条SELECTyuju 抓取当前对象的关联对象的数据。即加载延迟。

Join:它的策略是在同一条SELECT语句使用内连接来获得对象的数据和它的关联对象的数据,此时关联对象的延迟加载失效。

以下映射文件是单端关联上fetch=jion的一个配置示例。

<?xml version=”1.0” encoding=”utf-8”?>

<!DOCTYPE hibernate-mapping PUBIC

       “-//Hibernate/Hibernate Mapping DTD 3.0//EN”

       “http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd”>

<hibernate-mapping>

       <!--  映射持久化类  --  >

       <class name=”com.qiujy.domain.Order” table=”order”>

       <!--  映射对象标示符  --  》

       <id name=”id” column=”id” ><generator class=”native”/></id>

       <!--  映射普通属性 --  >

       <property name=”orderNo”/>

       <property name=”createdtime” column=”created_time”type=” timestamp”/>

       <!--  映射多对一关联,添加fetch属性  --  >

       <many-to-many name=”account” column=”account_id” fetch=”join” not-null=”true”/>

</class>

</hibernate-mapping>

在应用程序中加载某个Order实体数据时,会使用内连接把它关联的Account实体也加载上来,即Hibernate会产生如下一SQL语句。

Select

              order0_.id as id-_1_,

              order0_.orderNo as orderNo1_1_,

order0_.created_time as created3_1_1-,

              order0_.account_id as account4_1_1_,

              account1_.id as id0_0_,

              account1_.login_name as login2_0_0_from

       from

              orders order0_

       inner join

              account account1_

                     on order_0.account_id=account1_.id

       where

              order0_.id=?

集合属性上的抓取策略

在集合属性的映射元素上可以添加fetch属性,它有三个可选值。

Select:作为默认值,它的策略是当需要使用所关联集合的数据时,另外单独发送一条SELECT语句使用内连接来获得对象的关联集合,此时关联集合上的lazy会失效。

Subselect:另外发送一条查询语句(或子查询)抓取在前面查询到的所有实体对象的关联集合。这个策略对HQL的查询也起作用。

以下映射文件就是集合属性上fetch=subselect的一个配置示例。

<?xml version=”1.0”?>

<!DOCTYPE hibernate-mapping PUBLIC

       “-//Hibernate/Hibernate Mapping DTD 3.0//EN”

       “http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd:>

<hibernate-mapping>

       <!--  映射持久化类 --  >

       <class name=”com.qiujy.domain.Account” table=”account”>

       <!—映射对象标示符 -- >

       <id name=”id” column=”id”><generator class=”native”/></id>

       <!—映射普通属性 --  >

       <property name=”loginName” column=”login_name”/>

       <! --  用集合来映射一对多关联,并指定fetch属性--  >

       <set name=”orderSet” cascade=”all” inverse=”true” fetch=”subselect”>

       <key column=”account_id”/>

       <one-to-many class=”com.qiujy.domain.Order:/>

       </set>

</class>

</hibernate-mapping>

当使用get()或load()方法加载一个Account实体数据时,对它关联的Order集合属性先延迟加载,当真正需要使用Order集合属性中的数据时,才载发一条SQL语句来抓取数据;当使用HQL语句加载多个Account实体数据时,对它们关联的Order集合属性先延迟加载,当真正需要使用Order集合属性中的数据时,才会再发一条子查询语句来抓取相应的数据。

       以上就是关于抓取策略的具体介绍,在实际项目开发过程中,不仅需要根据实际的情况选择合适的抓取策略,而且需要通过不断地测试来验证这个策略是不是最有效率的。

      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值