Hibernate的检索策略
hibernate 的中的session依照load()和get()按照参数的制定OID(ObjctID)去加载一个持久化对象。另外Query.list()方法则按照HQL语句去加载持久化的对象。
以上的几个方式都可以实现hibernate的持久化对象的加载。如果有不懂hibernate对象的"临时状态"、"游离态"、"持久态"、"删除状态"的小伙伴,自己先去了解下。
对于hibernate的以上的几种检索策略,我们先来了解下他们几种的工作细节。
首先是session的load()方法,我们以Customer和Orders的例子来简单说明下。Customers与Orders的关系是一对多的关系。我们这边假定已经配置了这两个对象hbm.xml的一对多单向关联关系。
一、session的load(Customers.class,new Long(1))的方式返回的是一个Customer持久化对象的引用。这时候Customer还没有初始化。因此时不会出现sql查询语句的执行。要等到真正使用到Customer的时候才会进行初始化。也就是执行查询语句。这时候如果在Custmoers 中配置了
2 name="orders"
3 insever="ture"
4 lazy="true"
5 fetch= "select"
6 <one-to-many class=....>
7 >
lazyd的默认属性是true,也就是说采用懒加载。
1、如果lazy设置为false的话,则会在初始化Customers对象的时候就会去将Customers所有Orders对象进行查询,这也叫立即检索。这里有个地方得值得注意的是,如果上面的set标签中配置了fecth属性为join,这时候load在真正执行查询的时候,关联对象的时候使用的的是迫切左外连接查询,这样的话,就算是显式的设置lazy的属性,也无意义。至于为什么,我想熟悉sql查询的都知道原因了。当然fecth属性还有其他的一些选项例如select,subselect,fetch默认为select。
2、如果采用懒加载,也就是lazy设置为true的话,则在初始化Customers只会对customers对象进行查询操作。而在真正使用Orders属性时才对Customers关联的Orders对象进行查询操作。这里得说明下:例如 在对Order order = customer.iterator().next()迭代的时返回是Orders的代理实例。这时的Order还没有进行实例化,所以没有进行sql查询。只有在对其属性或方法调用的时候,例如获取订单号:Order.getOrderNo()的时候才对其进行实例化。
lazy还有其他选项 extra增强 ,例如获取一个订单集合的size/contains等方法时,这时不会去初始化该orders,而是会向数据库发送一条sql去查询获取值,只有真正要用到集合元素值时才去实例化查询数据库。这种比较聪明的方式与lazy设置为true的区别就是在选择懒加载的时机上extra更加“懒惰”,更加延迟。
二、session.get(Customers.class,new Long(1))。get采用的是立即检索的策略,这里Customers一定会立马初始化。
1、 如果在对Orders的配置中,set中设置了lazy为true的话,则与load类似。等到真正使用Orders的属性的时候才会去做sql关联查询。
2、 如果 lazy设置为false,则在get()方法执行的时候也会立马初始化Orders的所有关联对象。这种方式与上面load的第一种方式唯一的区别在与,load初始化比get的初始化Customers的时机更晚,更延迟。
这里注意的是,如果如果上面的set标签中配置了fecth属性为join,关联对象的时候使用的的是迫切左外连接查询。不管lazy是否设置为true,采用的也是立即加载。
三、query接口中的list()方法。 seesion.createQuery(sql).list(),执行该方法后,程序会里面向数据库发送sql去执行查询操作。
1、 如果在对Orders的配置中,set中设置了lazy为true的话,等到真正使用Orders的属性的时候才会去做sql关联查询。
2、 如果 lazy设置为false的话,则会立马初始化Orders的所有关联对象。这个类似于get()方式。
这里有个地方需要注意下,在query.list()的时候,它会fetch的join策略的,所以就算是显式的设置了fetch为join,也不会采用迫切左外联接查询。
总结下load,get,query.list三种方式的区别:
1.load在对类级别的对象加载是有“延迟”作用的,而get和query.list采用的是立即检索的策略。
2.在关联一对多关联关系的加载时,load与get和query.list 采用的都是同样的策略,前提是在many一方的懒加载方式也是一致。
3.在get和load方式加载时,对fetch的策略是一致的。 而query.list是对fetch的join的策略是忽略的。
对于一对多的单向关联关系的懒加载策略做了大致的分析。其实双向也很好理解。
假设在Orders.hbm.xml文件中的<many-to-one> 中配置了以下配置:
1 <many-to-one
3 lazy = "proxy"
4 ....
5 class="..."
6 />
这里前提是在Customers.hbm.xml文件中的set的inserver设置为false或者不显式设置。默认为false。
1、这时在对orders对象进行加载的时候,如果customer采用的lazy是proxy方式,则采用代理懒加载方式。也就说,只有当orders获取它的custormer属性的时候,才去加载与之关联的customer对象。如果order的lazy采用的也是懒加载方式的话,则在获取customer时不会获取orders对象。
举个例子:在get(orders.class,new Long(1))的方法获取Orders的时候,会立马加载Orders对象,当获取Orders.getCustomer().getName()的时候,才会去加载Customer对象,而如果再去获取该Customer对象下的Orders的时候,才去加载Orders。也就是说会去执行3次的sql,而每次执行一条sql语句。
而如果Orders采用lazy为false的话,则与上面不用。则会在立马加载Orders,而后会在加载Customers的同时一起加载Orders对象。也就说这里也会执行3条sql, 但是分为2次执行,第一次一条查询orders,第二次两条,执行查询Customer和Orders的查询。
2、如果customer采用的lazy是no-proxy方式,则证明采用非代理懒加载,这个与上面的区别在于这种方式是,在对Orders加载完后,只有一旦获取customer属性时返回的就是Customers的实例,而不是代理实例。这样就会立马对Customers进行实例化。其他的与proxy的一致。也就是说区别在于对Customers对象的延迟加载时机的策略不同。
3、lazy的选项还可以是false,也就说。在对Orders进行加载的时候,会立马对Customers进行立刻检索。也就说会里面发出3条sql语句。2次查询order,一次查询customer。
关于hibernate的懒加载,这里讲的只涉及到了一对多的关联关系代码不多,但懂的人应该可以理解。还有一对多,一对一,多对多。。hibernate都有自己的策略,而且都很灵活。后续会给大家补上。