本篇主要有get和load的类级别查询方法,懒加载原理,关联级别查询(集合级别,属性级别)中的fetch属性和lazy属性。
一、类级别查询
1.1、get方法和load方法
get方法:使用get方法,立即加载,即执行该方法时,立即发送SQL语句查询结果。
load方法(懒加载/延迟加载):默认情况下,在执行load方法时,不发送任何SQL语句,返回一个对象,而是在使用该对象时,才发送SQL查询。
延迟加载:仅仅获得没有使用,不会查询,在使用时才会进行查询!
上述说“load默认情况下”,对,load还可以不是默认情况,在ORM配置文件(×××.hbm.xml)中的class元素有一个lazy属性,默认为true,就是懒加载,若改为false,再使用load方法时它就“不懒了”,会立即加载,与get的效果完全相同!
!!!get方法就是单纯的立即加载,load才分两种情况,别弄混了!!!
1.2、load懒加载原理
在上面使用load截图中的这一行加断点,debug一下,会发现Variables窗口看到对象c,它的value里包含有两个$符:
如果之前学过动态代理就会知道,对象在打印时,若出现了$符,该对象就是一个代理对象(对自己的方法进行增强或改造)。所以这里对象c变成了一个代理对象(hibernate已经帮我们创建好了),对于现在这个c对象,不再是一个普通的Customer对象了,而是可以根据当前session,具有了查询数据库的功能。
c->handler->initialized属性的值为false,表示没有初始化,按F8往下继续执行一句,查询数据库了,initialized属性值立即变成了true。
注意:懒加载时,调用属性加载数据要确保session还是打开的(上面解释了,代理对象c是根据当前session查询数据库的)
在session关闭后使用c对象,会抛出如下异常:
如果在session关闭前,使用了c对象,关闭后再使用session,这种情况下(如下图)不会报异常,因为c对象已经被初始化了,session关闭之后还是可以使用c对象的。不过这时的c对象是游离态,在事务中,不会有这种情况。
下面对于类级别查询的懒加载小结一下:
lazy:为true(默认)时,查询类时,会返回代理对象,该对象会在使用属性时,根据关联的session查询数据库,加载数据;为false时,load方法与get方法没有区别,一调用就会加载数据。为了提高效率,建议使用懒加载!
二、关联级别查询
这部分用了上一篇搭建好的环境,看前请参考我上一篇中的一对多关系的部分!!! 上面的get和load方法都是根据id查询单表,太过于简单了。下面来分析关联级别的查询,集合关联查询又分为集合关联查询和属性关联级别查询。
2.1、集合关联级别查询
在配置set元素时,它还有lazy属性和fetch属性,在集合关联查询中,这两个属性各有三个取值。
lazy(决定是否延迟加载):
①true(默认值)——延迟加载 ②false——立即加载 ③extra——极其懒惰
fetch(决定加载策略,使用什么类型的SQL语句加载集合数据):
①select(默认值)——单表查询加载 ②join——使用多表查询集合 ③subselect——使用子查询加载集合
2.1.1、单表查询(fetch="select")
(1)fetch="select" lazy="true"
先改一下Customer.hbm.xml里的set配置:
再测试(在19行和21 行分别加上了断点):
分析:
执行到19行,查询Customer,根据控制台打印情况,可以看出只加载了Customer,并没有加载linkMens;
执行20行,获得linkMens,控制台打印结果没有任何变化;
执行21行,使用LinkMens,控制台加载了LinkMens,并显示了结果。
小结:lazy为true时是懒加载,使用时才加载集合数据;上述SQL语句都是单表查询语句,所以fetch为select时是单表查询。
(2)fetch="select" lazy="false"
改一下Customer.hbm.xml配置,测试代码不变:
分析:
执行19行,加载了Customer之后立即加载了linkMens;
执行20行,无变化;
执行21行,显示了结果。
小结:lazy为false时是立即加载集合数据;fetch为select时是单表查询。
(3)fetch="select" lazy="extra"
添加一行打印集合size的测试代码:
分析:
执行32行,查询Customer,只加载了Customer,并没有加载linkMens;
执行33行,无变化;
执行34行,直接查询了集合的size,没有去加载linkMens;
执行35行,这时才加载了linkMens,并显示结果。
小结:lazy为extra时,极其懒惰和懒加载效果基本一致,如果只获得集合的size,就只查询集合的size(count语句),节省了很多资源;fetch为select时是单表查询。
2.1.2、单表查询(fetch="join")
(4)fetch="join" lazy="true"
为方便看到是多表查询,在主配置文件里设置了格式化SQL : <property name="hibernate.format_sql">true</property>
分析:
执行32行,查询Customer,不仅加载了Customer,还加载了与Customer关联的linkMens;
执行33,34,35行时没有打印SQL语句,而是直接打印了结果(SQL语句太长就没有截图):
小结:fetch为join时,为多表查询,此时,lazy属性设置为true/false/extra都一样,都是立即加载。
简言之,fetch为join时,lazy属性失效!
(5)fetch="join" lazy="false"
(6)fetch="join" lazy="extra"
略过。。。。。。。。。。。。
2.1.3、子查询(fetch="subselect")
子查询:查询嵌套,一个查询需要用到另一个查询的结果
所有测试代码需要改变了
(7)fetch="subselect" lazy="true"
在51行设置断点
执行51行,打印了查询所有Customer的语句,并没有加载list集合;
执行54行,打印Customer;
执行55行出现了子查询,这时使用到了list集合,才加载list集合;
小结:fetch为subselect时,为子查询,lazy属性设置为true,懒加载。
(8)fetch="subselect" lazy="false"
执行51行时,打印了查询所有Customer的语句,并加载list集合,(7)中两个很长的查询语句都一起打印了,太长了,就不再截图了;
小结:fetch为subselect时,为子查询,lazy属性设置为true,立即加载。
(9)fetch="subselect" lazy="extra"
与lazy=“true”不同的是:
在执行55行时,只打印了select count()语句,并未加载list,只有执行到了56行,才加载list
小结:lazy为extra时,极其懒惰和懒加载效果基本一致,如果只获得集合的size,就只查询集合的size(count语句);fetch为select时是子查询。
2.2、属性关联查询
在LinkMan.hbm.xml配置如下(蓝色高亮部分),去掉Customer中对于这两个属性的配置!
与集合关联不同的是,fetch只有两个取值,没有子查询,且lazy三个取值也和集合关联的不同,具体如下:
lazy(决定是否延迟加载):
①proxy(默认值)——"别人替我"决定 ②false——立即加载 ③no-proxy(略过)
(用例子解释一下proxy值:由Customer的类级别加载策略决定,即Customer配置文件中class元素的lazy属性决定)
fetch(决定加载策略,使用什么类型的SQL语句加载集合数据):
①select(默认值)——单表查询加载 ②join——使用多表查询集合
这里我们只考虑以下四种情况:
2.2.1、单表查询
(1)fetch="select" lazy="proxy"(都是默认值)
测试代码:
**************Customer配置文件中class属性配置lazy为“true”:***********************
分析:
执行68行,加载了LinkMan,并未加载Customer;
执行69行,无变化;执行70行,使用到了Customer,才加载Customer。
***********************Customer配置文件中class属性配置lazy为“false”:***********************
分析:
执行68行,加载了LinkMan后马上加载Customer; 小结:fetch值为select表示单表查询毋庸置疑,lazy属性为proxy时,是根据Customer配置文件中class元素的lazy属性决定的,lazy为true时就是懒加载,为false就是立即加载。
(2)fetch="select" lazy="false"
与(1)中Customer配置文件中class元素的lazy取“false”一样,都是立即加载。
2.2.2、多表查询
和集合关联查询中多表查询一样,此时lazy属性已经失效了!
总结:为了提高效率,fetch的选择上应该选择select,lazy的取值应该选择true,即全部选择默认值!