3.1 延迟加载
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>
当运行到p的时候,全部加载了,执行语句如下:
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”>
School的lazy属性自然还是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.(如果你整个类不是lazy=true的话,hibernate会忽视你某个属性的property)
应该是因为,我们并未用到编译时字节码增强技术的原因。如果只对部分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.
4 集合无关联
Person类
1publicclass Person { 2private String name;3private String sex;4private Setaddresses;5 }
Person.hbm.xml
1<class name="com.hbm.hibernate.Person" table="PERSON">
2<id name="name" type="java.lang.String">
3 <column name="NAME"/>
4 <generator class="assigned"/>
5</id>
6<property name="sex" type="java.lang.String">
7 <column name="SEX"/>
8</property>
9<set name="addresses" table="ADDRESSES" inverse="false" lazy="true" fetch="join">
10 <key column="NAME"/>
11 <element column="ADDRESS" type="java.lang.String"></element>
12</set>
13</class>
4.1 非延迟加载策略
映射文件的配置<set lazy=”false”>。
1 tx = session.beginTransaction();
2 Person person=(Person) session.load(Person.class, "XiJinping");//(1)
3 System.out.println("");//(2)
4 System.out.println("0: "+person.getName());//(3)
5 System.out.println("1: "+person.getSex());//(4)
6 System.out.println("2: "+person.getAddresses());//(5)
7 tx.commit();
运行到(4)处时,加载了全部属性,执行了如下sql语句。
1Hibernate:
2/* load com.hbm.hibernate.Person */
select person0_.NAME as NAME0_0_, person0_.SEX as SEX0_0_ from PERSON person0_ where person0_.NAME=?
Hibernate: 10/* load collection com.hbm.hibernate.Person.addresses */
select addresses0_.NAME as NAME0_, addresses0_.ADDRESS as ADDRESS0_ from ADDRESSES addresses0_ where addresses0_.NAME=?
fetch策略的配合使用,当<set lazy=”false” fetch=”join”>时,执行的sql语句如下。这个是有,将不再采用两条select语句的方式,而是采用左连接的方式进行,有利于提高效率。
Hibernate: /* load com.hbm.hibernate.Person */
select person0_.NAME as NAME0_0_, person0_.SEX as SEX0_0_, addresses1_.NAME as NAME2_, addresses1_.ADDRESS as ADDRESS2_ from PERSON person0_ leftouterjoin ADDRESSES addresses1_ on person0_.NAME=addresses1_.NAME where person0_.NAME=?
4.2 延迟加载策略
映射文件的配置<set lazy=”true”>。
当程序运行到(4),hibernate加载了Person对象的其他全部属性,执行了如下sql语句。
Hibernate: /* load com.hbm.hibernate.Person */select person0_.NAME as NAME0_0_, person0_.SEX as SEX0_0_ from PERSON person0_ where person0_.NAME=?
当程序运行到(5)时,hibernate加载了所有的address对象,执行如下sql语句。
1Hibernate: 2/* load collection com.hbm.hibernate.Person.addresses */select3 addresses0_.NAME as NAME0_,4 addresses0_.ADDRESS as ADDRESS0_ 5from6 ADDRESSES addresses0_ 7where8 addresses0_.NAME=?
4.2 延迟加载extra
It can also be used to enable "extra-lazy" fetching where most operations do not initialize the collection. This is suitable for large collections.
大部分操作的时候并不会加载集合,适用于大的集合。extra其实是一种比较智能的延迟加载,即调用集合的size/contains等方法的时候,hibernate并不会去加载整个集合的数据,而是发出一条聪明的SQL语句,以便获得需要的值,只有在真正需要用到这些集合元素对象数据的时候,才去发出查询语句加载所有对象的数据。
映射文件配置映射文件的配置<set lazy=”extra”>
1publicint getNum(){ 2return addresses.size(); 3} 4 5 tx = session.beginTransaction(); 6 Person person=(Person) session.load(Person.class, "XiJinping");//(1) 7 System.out.println("");//(2) 8 System.out.println("0: "+person.getName());//(3) 9 System.out.println("1: "+person.getSex());//(4)10 System.out.println("2: "+person.getNum());//(5)11 System.out.println("3: "+person.getAddresses());//(6)12 tx.commit();
当程序运行到(4)时,进行了第一次的加载,加载了person对象的所有普通属性,执行sql如下:
Hibernate: /* load com.hbm.hibernate.Person */select person0_.NAME as NAME0_0_, person0_.SEX as SEX0_0_ from PERSON person0_ where person0_.NAME=?
当程序运行到(5)时,进行了第二次加载,这个时候并没有去加载set集合中的所有属性,hibernate智能的用sql语句获取了集合中的数量,执行的sql语句如下:
Hibernate: selectcount(ADDRESS) from ADDRESSES where NAME =?
当程序运行到(6)时,进行了第三次加载,将集合中的所有对象均加载进来了,执行的sql语句如下:
Hibernate: /* load collection com.hbm.hibernate.Person.addresses */select addresses0_.NAME as NAME0_, addresses0_.ADDRESS as ADDRESS0_ from ADDRESSES addresses0_ where addresses0_.NAME=?
4.4 总结
在集合的3中延迟加载中,我觉得最有的配置应该是extra。但是,默认配置false和extra均不适用于,session会话之外的情况。
Hibernate中集合属性的延迟加载应该来说是最为重要的,因为如果集合属性里面包含十万百万记录,在初始化持久实体的同时,完成所有集合属性的抓取,将导致性能急剧下降。
5 集合有关联
Person类
1publicclass Person { 2private String personId;3private String name;4private Set addresses;5publicint getNum(){ 6return addresses.size();7 }8 }
Address类
1publicclass Address { 2private String addressId;3private String addressDetail;4private Set people;5 }
Person.hbm.xml
………
5.1 非延迟加载
映射文件配置<set lazy=”false”>
1 tx = session.beginTransaction();2 Person person=(Person) session.load(Person.class, "001");//(1)3 System.out.println("");//(2)4 System.out.println("0: "+person.getPersonId());//(3)5 System.out.println("1: "+person.getName());//(4)6 System.out.println("2: "+person.getNum());//(5)7 System.out.println("3: "+person.getAddresses());//(6)8 tx.commit();
当程序运行到(4)时,hibernate加载了所有属性,执行的sql语句如下:
Hibernate: select person0_.PERSONID as PERSONID2_0_, person0_.NAME as NAME2_0_ from PERSON person0_ where person0_.PERSONID=?Hibernate: select addresses0_.PERSONID as PERSONID1_, addresses0_.ADDRESSID as ADDRESSID1_, address1_.ADDRESSID as ADDRESSID0_0_, address1_.ADDRESSDETAIL as ADDRESSD2_0_0_ from PERSON_ADDRESS addresses0_ leftouterjoin ADDRESS address1_ on addresses0_.ADDRESSID=address1_.ADDRESSID where addresses0_.PERSONID=?
5.2 延迟加载与extra策略
与无关联关系时一致,不再累述。
6 1-1和N-1延迟加载策略
LineItem类
publicclass LineItem { privateint lineNumber; privateint amount; privatedouble price;private Product product;}
Product类
publicclass Product { private String id; private String name;privatedouble listprice;}
LineItem.hbm.xml
6.1 非延迟加载
映射文件配置<many-to-one lazy=”false”>
1 tx = session.beginTransaction();2 LineItem l=(LineItem) session.load(LineItem.class, 2);//(1)3 System.out.println("");//(2)4 System.out.println("0: "+l.getLineNumber());//(3)5 System.out.println("1: "+l.getAmount());//(4)6 System.out.println("2: "+l.getProduct());//(5)7 tx.commit();
程序运行到(4)处时,hibernate加载了所有属性,执行了如下sql语句:
Hibernate: select lineitem0_.LINENUMBER as LINENUMBER1_0_, lineitem0_.AMOUNT as AMOUNT1_0_, lineitem0_.PRICE as PRICE1_0_, lineitem0_1_.PRODUCTID as PRODUCTID2_0_ from LINEITEM lineitem0_ innerjoin LINE_PRODUCT lineitem0_1_ on lineitem0_.LINENUMBER=lineitem0_1_.LINENUMBER where lineitem0_.LINENUMBER=?Hibernate: select product0_.PRODUCTID as PRODUCTID0_0_, product0_.NAME as NAME0_0_, product0_.LISTPRICE as LISTPRICE0_0_ from PRODUCT product0_ where product0_.PRODUCTID=?
在这个时候,去查看内存中的LineItem类型对象,我们发现也是一个代理类。而回调函数中,tagert属性中的Prdouct是一个真正的Product类型对象。
6.2 延迟加载proxy
映射文件设置<many-to-one lazy=”proxy”>
当程序运行到(4)时,进行了第一次的加载,执行的sql语句如下:
Hibernate: select lineitem0_.LINENUMBER as LINENUMBER1_0_, lineitem0_.AMOUNT as AMOUNT1_0_, lineitem0_.PRICE as PRICE1_0_, lineitem0_1_.PRODUCTID as PRODUCTID2_0_ from LINEITEM lineitem0_ innerjoin LINE_PRODUCT lineitem0_1_ on lineitem0_.LINENUMBER=lineitem0_1_.LINENUMBER where lineitem0_.LINENUMBER=?
当程序运行到(5)时,进行了第二次的加载,执行的sql语句如下:
Hibernate: select product0_.PRODUCTID as PRODUCTID0_0_, product0_.NAME as NAME0_0_, product0_.LISTPRICE as LISTPRICE0_0_ from PRODUCT product0_ where product0_.PRODUCTID=?
这个时候,我们去参看内存,发现target中的product属性便是个代理类,如下图所示:
6.3 总结
默认情况下,Hibernate 也会采用延迟加载来加载关联实体,不管是一对多关联、还是一对一关联、多对多关联,Hibernate 默认都会采用延迟加载。
对于关联实体,可以将其分为两种情况:
关联实体是多个实体时(包括一对多、多对多):此时关联实体将以集合的形式存在,Hibernate 将使用 PersistentSet、PersistentList、PersistentMap、PersistentSortedMap、PersistentSortedSet 等集合来管理延迟加载的实体。这就是前面所介绍的情形。
关联实体是单个实体时(包括一对一、多对一):当 Hibernate 加载某个实体时,延迟的关联实体将是一个动态生成代理对象。
当关联实体是单个实体时,也就是使用 <many-to-one.../> 或 <one-to-one.../> 映射关联实体的情形,这两个元素也可通过 lazy 属性来指定延迟加载。
7 继承(subclass为例)
Payment类
1publicclass Payment { 2privatelong id;3privatelong amount;4 }
CreditCardPayment类
publicclass CreditCardPayment extends Payment { privatelong creditId;private String cardType;}
creditCardPayment.hbm.xml
8.1 非延迟加载
映射文件配置<subclass lazy=”false”>。
1 tx = session.beginTransaction();2 CreditCardPayment ccp=(CreditCardPayment) session.load(CreditCardPayment.class,new Long(8889));//(1)3 System.out.println("");//(2)4 System.out.println("0: "+ccp.getId());//(3)5 System.out.println("1: "+ccp.getAmount());//(4)6 System.out.println("2: "+ccp.getCardType());//(5)7 tx.commit();
程序运行到(1)时,加载全部属性,执行的sql语句如下:
Hibernate: select creditcard0_.ID as ID0_0_, creditcard0_.AMOUNT as AMOUNT0_0_, creditcard0_.CREDITID as CREDITID0_0_, creditcard0_.CARDTYPE as CARDTYPE0_0_ from PAYMENT creditcard0_ where creditcard0_.ID=? and creditcard0_.PAYMENT_TYPE='CREDIT'
7.2 延迟加载
映射文件配置<subclass lazy=”true”>
Hibernate: select creditcard0_.ID as ID0_0_, creditcard0_.AMOUNT as AMOUNT0_0_, creditcard0_.CREDITID as CREDITID0_0_, creditcard0_.CARDTYPE as CARDTYPE0_0_ from PAYMENT creditcard0_ where creditcard0_.ID=? and creditcard0_.PAYMENT_TYPE='CREDIT'
程序执行到(4)时,第一次加载全部属性,执行的sql语句如上。
7.3 总结
继承方式的延迟加载,set等在true或false时并未显著差别,在这里不再累述。