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 中配置了
1<set2name="orders"3insever="ture"4lazy="true"5 fetch= "select"
6 <one-to-manyclass=....>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
2insever="true"3lazy="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都有自己的策略,而且都很靈活。后續會給大家補上。
新聞熱點
疑難解答