• Hibernate Lazy属性与懒加载 整理


    lazy概念:要用到的时候,再去加载,对于关联的集合来说,只有当访问到的时候,才去加载它所关联的集合,比如一个user对应很多权限,只有当user.getRights()的时候,才发出select right的语句,在访问到rights之前,rights是一个PersisitSet对于实体类来说,只有当它的属性被访问到时,才会真正加载这个实体类,在它的属性没有被访问到之前,这个实体类是一个代理对象。

    1.在集合中定义:<set><list>标签上

    ,可以取值:true/false/extra

    <set name="name" lazy="true/false/extra" >
    默认为true

    默认为true情况下,当使用到了Set对象,才会把整个set全部查询出来。

    false情况下,不使用Lazy,查询Lazy所属的对象时,set就会被查询上来。extra情况下,比较智能,根据查询的内容,生成不同的SQL语句。效率会高一些。

    例子:在我们前边多对一的关系中(部门与员工):

    Department.hbm.xml

    [html] view plain copy

     print?

    1. <set name="emps" inverse="true" lazy="false">  
    2.             <key column="depart_id" />  
    3.             <one-to-many class="Employee" />  
    4.         </set>  

    通过这个可以关闭默认的懒加载

    2单端关联 <one-to-one><many-to-one>单端关联上,可以取值:false/proxy/no-proxy

    <many-to-one name="name" lazy="false/proxy/no-proxy">
    默认为proxy

    false:不使用Lazy。此关联总是被预先抓取

    proxy:使用懒加载

    no-proxy:指定此属性应该在实例变量第一次被访问时应该延迟抓取(fetche lazily

    [html] view plain copy

     print?

    1. <many-to-one name="depart" column="depart_id" lazy="false"/>   

     

    • lazy="proxy" applies to single objects (ie foo.SingleBar)
    • lazy="true" applies to collections of objects (ie foo.MultiBar)

    (You can't set lazy="proxy" to a collection, nor can you set lazy="true" to a single reference. Either will cause NH to throw a XmlSchemaException which is a little cryptic to beginners.)

     

     

    比如说在college.hbm.xml里面写上

    <set name="majors" inverse="true" lazy="false" cascade="delete">

    加载学院时立刻加载学院的专业,那么在查询所有学院时,产生的sql语句如下:

    Hibernate: select majors0_.college_id as college_2_3_0_, majors0_.major_id as major_id1_6_0_, majors0_.major_id as major_id1_6_1_, majors0_.college_id as college_2_6_1_, majors0_.major_name as major_na3_6_1_, majors0_.major_code as major_co4_6_1_ from studorm.tb_major majors0_ where majors0_.college_id=?

     

     

    在查询所有学院的时候,会立刻加载每个学院的专业,所以如非必要,不要加上

    十分浪费资源

     

     

    以下来自:http://www.cnblogs.com/wukenaihe/archive/2013/06/11/3131640.html

     

    3.class标签

    除了用在<set>  <one-to-one><many-to-one>标签上,lazy还能用在

    * <class>标签上,可以取值:true/false ,在hibernate3以上版本,默认是true
    * <property>
    标签上,可以取值:true/false

     

    在<class>标签上,可以取值:true/false ,在hibernate3以上版本,默认是true

     

    默认为true,可不写,在执行查询语句时不进行,比如session.load(id)时,不执行sql语句(session.get(id)不支持lazy),而是在具体获取参数时,执行sql语句,比如obj.getName()。

     

    <class>标签上的lazy特性只对普通属性起作用

     

    <class>标签上的lazy不会影响到单端关联上的lazy特性

     

    3.1 延迟加载策略(默认)

      如果想对实体对象使用延迟加载,必须要在实体的映射配置文件中进行相应的配置

      <class name="Person" table="PERSON" lazy="true">

    1 tx = session.beginTransaction();

    2 Person p=(Person) session.load(Person.class, "001");//(1)

    3 System.out.println("0: "+p.getPersonId());//(2)

    4 System.out.println("0: "+p.getName());//(3)

    5 tx.commit();

    6 session.close();

      执行到(1)并没有出现sql语句,并没有从数据库中抓取数据。这个时候查看内存对象p如下:

    2.1 person对象load时的内存快照

      观察person对象,我们可发现是Person$$EnhancerBy..的类型的对象。这里所返回的对象类型就是Person对象的代理对象,在hibernate中通过使用CGLB来先动态构造一个目标对象的代理类对象,并且在代理对象中包含目标对象的所有属性和方法。所以,对于客户端而言是否为代理类是无关紧要的,对他来说是透明的。这个对象中,仅仅设置了id属性(即personId的值),这是为了便于后面根据这个Id从数据库中来获取数据。

       运行到(2)处,输出为001,但是仍然没有从数据库里面读取数据。这个时候代理类的作用就体现出来了,客户端觉得person类已经实现了(事实上并未创建)。但是,如果这个会后session关闭,再使用person对象就会出错了。

       调试运行到(3)处,要用到name属性,但是这个值在数据库中。所以hibernate从数据库里面抓取了数据,sql语句如下所示:

    Hibernate:

    select

    person0_.PERSONID as PERSONID3_0_,

    person0_.NAME as NAME3_0_

    from

    PERSON person0_

    where

    person0_.PERSONID=?

      这时候,我们查看内存里面的对象如下:

    2.2 class延迟加载时内存对象

      真正的Person对象放在CGLIB$CALLBACK_0对象中的target属性里。

      这样,通过一个中间代理对象,Hibernate实现了实体的延迟加载,只有当用户真正发起获得实体对象属性的动作时,才真正会发起数据库查询操作。所以实体的延迟加载是用通过中间代理类完成的,所以只有session.load()方法才会利用实体延迟加载,因为只有session.load()方法才会返回实体类的代理类对象。

    3.2 非延迟加载策略

      Hibernate默认的策略便是非延迟加载的,所以设置lazy=false

        

    1 tx = session.beginTransaction();

    2 Person p=(Person) session.load(Person.class, "001");//(1)

    3 System.out.println("0: "+p.getPersonId());//(2)

    4 System.out.println("0: "+p.getName());//(3)

    5 tx.commit();

    6 session.close();

      调试运行到(1)处时,hibernate直接执行如下sql语句:

    Hibernate:

    select

    person0_.PERSONID as PERSONID3_0_,

    person0_.NAME as NAME3_0_

    from

    PERSON person0_

    where

    person0_.PERSONID=?

      我们在查看内存快照如下:

          这个时候就不是一个代理类了,而是Person对象本身了。里面的属性也已经全部普通属性也全部被加载。这里说普通属性是因为addresses这个集合对象并没有被加载,因为set自己本身也可以设置lazy属性。所以,这里也反映出class对象的lazy并不能控制关联或集合的加载策略。

    2.3 总结

      Hibernate<class lazy="">默认为true。如果,在load的时候只会返回一个代理类,并不会正在从数据库中读取数据。第一次用到时,会将所有普通属性set这种就不是)全部加载进来。如果第一次使用到时,session已经关闭将发生错误。

      如果显式是设置lazy=falseload的时候即会把所有普通属性全部读取进来。而且,返回的将是一个真正的该类型的对象(Person),而不是代理类。

    4字段加载(property)

      在Hibernate3中,引入了一种新的特性——属性的延迟加载,这个机制又为获取高性能查询提供了有力的工具。在大数据对象读取时,如Person对象中有一个School字段,该字段是一个java.sql.Clob类型,包含了用户的简历信息,当我们加载该对象时,我们不得不每一次都要加载这个字段,而不论我们是否真的需要它,而且这种大数据对象的读取本身会带来很大的性能开销。

    1  <class lazy="false">

      配置如下

    1 tx = session.beginTransaction();

    2 Person p=(Person) session.load(Person.class, "001");//(1)

    3 System.out.println("");//(2)

    4 System.out.println("0: "+p.getPersonId());//(3)

    5 System.out.println("0: "+p.getName());//(4)

    6 System.out.println("0: "+p.getSchool());//(5)

    7 tx.commit();

    1 <property name="name" type="java.lang.String">

    2 <column name="NAME" />

    3 </property>

    4 <property name="school" type="java.lang.String" lazy="true">

    5 <column name="SCHOOL"></column>

    6 </property>

           当运行到1的时候,全部加载了,执行语句如下:

    Hibernate:

    select

    person0_.PERSONID as PERSONID3_0_,

    person0_.NAME as NAME3_0_,

    person0_.SCHOOL as SCHOOL3_0_

    from

    PERSON person0_

    where

    person0_.PERSONID=?

      所有普通属性都均已加载。

    2<class lazy="true">

      Schoollazy属性自然还是true。当程序运行到(4)时,也同样加载了全部属性,执行了如下sql

    Hibernate:

    select

    person0_.PERSONID as PERSONID3_0_,

    person0_.NAME as NAME3_0_,

    person0_.SCHOOL as SCHOOL3_0_

    from

    PERSON person0_

    where

    person0_.PERSONID=?

      结果就是无效,不管采用何种策略都是无效的,和我们想想的有较大出路。下面是一段来自hibernate官方文档的话。

      Lazy property loading requires buildtime bytecode instrumentation. If your persistent classes are not enhanced, Hibernate will ignore lazy property settings and return to immediate fetching.

      应该是因为,我们并未用到编译时字节码增强技术的原因。如果只对部分property进行延迟加载的话,hibernate还提供了另外的方式,也是更为推荐的方式,即HQL或者条件查询。

      A different way of avoiding unnecessary column reads, at least for read-only transactions, is to use the projection features of HQL or Criteria queries. This avoids the need for buildtime bytecode processing and is certainly a preferred solution.

    实例

    User和rights是双向一对多,一个user有多条权限,在user.hbm.xml里这么配置

    <!-- 权限和用户的关系由权限来掌控,采用懒加载 -->

            <set name="rights" inverse="true"   cascade="delete" >

                <key>

                    <column name="user_rowcount" not-null="true" />

                </key>

                <one-to-many class="com.studorm.entity.Right" />

            </set>

    当通过user获取他的所有权限的时候,可以这么写:

    Session session=getCurrentSession();

          User user=(User)session.get(User.class, userRowcount);       

          Set<Right> rights=(Set<Right>)user.getRights();

    //注意user.getRights()返回PersisitSet是set的子类,不是HashSet的子类.HashSet<Right> rights=(HashSet<Right>)user.getRights()抛异常

          System.out.print("获取用户 "+user.getLoginId()+"的权限有"+rights.size()+"条: ");     

         

          Iterator<Right> iter = rights.iterator(); 

         

          while (iter.hasNext())

          { 

                 Right right= it.next(); 

                

    }

    产生的sql语句:

    Hibernate: select user0_.user_rowcount as user_row1_9_0_, user0_.manage_college_id as manage_c2_9_0_, user0_.login_id as login_id3_9_0_, user0_.login_pwd as login_pw4_9_0_, user0_.user_type as user_typ5_9_0_, user0_.user_phone as user_pho6_9_0_ from studorm.tb_user user0_ where user0_.user_rowcount=?

    Hibernate: select rights0_.user_rowcount as user_row2_9_0_, rights0_.right_rowcount as right_ro1_7_0_, rights0_.right_rowcount as right_ro1_7_1_, rights0_.user_rowcount as user_row2_7_1_, rights0_.function_id as function3_7_1_ from studorm.tb_right rights0_ where rights0_.user_rowcount=?

  • 相关阅读:
    2.由浅入深解析 SimpleDateFormat
    7.如何在Maven项目中引入自己的jar包
    6.Java英文缩写详解
    6.JXL操作Excel
    5.Apache POI使用详解
    4.Properties文件的加载和使用
    3.java.util.logging.Logger使用详解
    2.使用dom4j解析XML文件
    jdk、jre、spring、java ee、java se
    JVM架构
  • 原文地址:https://www.cnblogs.com/unflynaomi/p/5194880.html
Copyright © 2020-2023  润新知