hibernate對於對象的操作提供了很多的方法,本文簡單介紹一下這些方法的使用和比較.
在說明方法之前,說明一下hibernate中的對象的三種狀態,transient,persistent,detached.
transient:瞬態或者自由態.
persistent:持久化狀態.
detached:脫管狀態或者游離態.
狀態的判斷:
大體上來說,存在於session中的對象為persistent,從session中被clear,evict出來的對象(包括commit是被移出來的對象)是detached.建立的對象和delete的對象是transient狀態.但是一個建立的對象的如果有version欄位,並且version欄位unsaved-value不同,或者在id在由代碼賦值的時候有值,則認為是detached狀態.
1,persist和save方法:
按照spec,persist不保證立即執行SQL insert語句,save會立即執行insert語句.
在使用過程中,如果記錄的id不配置為由代碼賦值,如果不為 <generator class="assigned" />,如果對transient或者detached對象的oid賦上值,調用persist方法會有異常:
org.hibernate.PersistentObjectException: detached entity passed to persist:
如果用save方法的話,能夠儲存成功,賦的oid值不起作用.
如果想要賦的值起作用,用save(Object,ID)的方法.可以用replicate方法做類似的操作.
2,saveOrupdate不是根據資料庫有無記錄來做save或者update,而是根據對象的狀態,如果為transient對象,則save,如果是detached狀態,則update.總是會觸發hibernate session的save或者update.例如:如果記錄的oid不配置為由代碼賦值,如果不為 <generator class="assigned" />,如果對這個對象的oid賦了值,Hibernate總是發出一條SQL Update語句,如果資料庫沒有該oid對應的記錄,就不會更新任何記錄,如果oid的值為空白 的話,會進行save.
如果記錄的oid配置為由代碼賦值 ,為 <generator class="assigned" />,如果對應該oid的記錄不存在 ,就save,如果存在就update.在代碼中必須為oid賦值,否則會有異常:org.hibernate.id.IdentifierGenerationException:ids for this class must be manually assigned before calling save():
在同一個session中,對一個transient對象進行saveOrupdate,如果這個對象的oid正好和session中已經存在的對象oid相同,不論對id賦值配置為何種機制,這是候會有異常:
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
(上面這條規則同樣適用於update方法)
與update的區別是update總是根據對象的oid去update,對於update方法,如果沒有設oid會有異常:
org.hibernate.TransientObjectException: The given object has a null identifier:
在一個session中,對delete的obj進行update會有異常,如:
session.delete(obj);
....
session.update(obj);
但是對另一個session中(或者其他程式)delete的對象可以進行update,只是對資料庫沒有任何改動.
3,merge與update和saveorupdate的區別:從運行是看,最明顯的區別是如果update或者saveOrupdae的對象的oid和session中的某個對象相同,會有異常拋出,而merge不會.
如果merge的對象在資料庫不存在,會save一條記錄(和saveOrupdate類似).
如果session中存在相同oid的執行個體,會用merge的對象的狀態覆蓋舊有的持久執行個體.
Hbtest tbo = new Hbtest();
tbo.setId(new Integer(100));
tbo.setVal("val3");
sessionFactory.getCurrentSession().save(tbo);
Hbtest tbo1 = new Hbtest();
tbo1.setId(new Integer(100));
tbo1.setVal("val5");
sessionFactory.getCurrentSession().merge(tbo1); //update database
System.out.println("******* merge *******" + tbo.getVal()); //tbo的值為val5
如果session沒有相應的持久執行個體,則嘗試從資料庫中載入或建立一個新的持久化執行個體,merge方法會返回該持久執行個體
剛剛merge的這個對象沒有被關聯到session上,它依舊是游離態的.如果在merge後,對其做改變,值不會反映到資料庫.
Hbtest tbo1 = new Hbtest();
sessionFactory.getCurrentSession().merge(tbo1);
...
tbo1.setVal("new val");//不會更新到資料庫.
對返回的對象的改動會相應的更新資料庫
Hbtest tbo2 = (Hbtest)sessionFactory.getCurrentSession().merge(tbo1);
tbo2.setVal("new val");
merge的作用和資料庫提供的merge類似,如Oracle的merge into語句.
可以看出,只有在session中已經存在一個具有相同標識符的持久對象的時候,應該採用merge,此時用saveOrUpdate或者update會報錯.如果不確定當前session中是否已經有了具有相同標識符的持久對象,又想將當前的對象更新到(save 或者update)資料庫中,可以用merge.
4,replicate()方法完全使用給定對象各個屬性的值(包括oid)來持久化給定的游離狀態的實體,其中還需要指定儲存模式.replicate會先用select看資料是否在資料庫已經存在,如果存在,就update,否則save(在儲存模式為LATEST_VERSION或者OVERWRITE時).
與save的區別:如果id已經存在,save會有異常(主鍵衝突,org.hibernate.exception.ConstraintViolationException),在不是由代碼指定主鍵的時候,給save的對象賦的oid不會起作用,而由hibernate配置的機制負責.而replicate會用賦值的oid對資料庫進行操作.
與update的區別:比update多執行一條select語句.
在實際應用中用在,如果想把一個資料庫中的記錄複製到另一個資料庫中,可以用replicate方法,通過ReplicationMode來控制當資料衝突是的行為.
5,delete和evict
evict,從session的緩衝中去除當前執行個體.執行後對象的改變將不再和資料庫保持同步.當指定級聯風格為evict時,會級聯操作關聯對象.在用於大量操作的時候,清空緩衝,防止記憶體緊張.
delete,也會從session的緩衝中去除當前執行個體,但flunsh時會執行資料庫delete,之後對象就成了臨時狀態.
delete之後的對象不能調用update和merge方法,但是可以運用saveOrUpdate方法.
可以看出delete比起evict,不僅從session刪除,還會從資料庫刪除.
6,load和get
如果找不到合格紀錄,get()方法將返回null.而load()將會報出ObjectNotFoundEcception.如果你使用 load方法,hibernate認為該id對應的對象(資料庫記錄)在資料庫中是一定存在的,所以它可以放心的使用,它可以放心的使用代理來消極式載入該對象。在用到對象中的其他屬性資料時才查詢資料庫,但是萬一資料庫中不存在該記錄,那沒辦法,只能拋異常,所說的load方法拋異常是指在使用該對象的資料時,資料庫中不存在該資料時拋異常,而不是在建立這個對象時。由於 session中的緩衝對於hibernate來說是個相當廉價的資源,所以在load時會先查一下session緩衝看看該id對應的對象是否存在,不存在則建立代理,實際使用資料時才查詢二級緩衝和資料庫.所以如果你知道該id在資料庫中一定有對應記錄存在就可以使用load方法來實現消極式載入。
load()方法可以返回實體的代理類執行個體,而get返回的可能是實體類,也可能是代理類.get方法如果在session緩衝中找到了該id 對應的對象,如果剛好該對象前面是被代理過的,如被load方法使用過,或者被其他關聯對象消極式載入過,那麼返回的還是原先的代理對象,而不是實體類對象,如果該代理對象還沒有載入實體資料(就是id以外的其他屬性資料),那麼它會查詢二級緩衝或者資料庫來載入資料,但是返回的還是代理對象,只不過已經載入了實體資料。
7,query的list和iterate方法:list會一次取資料,iterate會先取id,再根據id多次取資料. refer
8,Session的clear,evict和flush方法
clear()方法清除Session層級緩衝中的所有實體(包括各種狀態)對象,目的是釋放記憶體.
evict()方法清除Session層級緩衝中的指定的實體(包括各種狀態)對象.
flush()強制持久化Session緩衝中的實體物件,不會從緩衝中清除對象.
sessionFactory.getCurrentSession().save(tbo1);
sessionFactory.getCurrentSession().flush();//執行SQL,如果在clear之前不執行flush,tbo1不會被儲存到資料庫
sessionFactory.getCurrentSession().clear();
sessionFactory.getCurrentSession().save(tbo1);
sessionFactory.getCurrentSession().flush();
sessionFactory.getCurrentSession().evict(tbo1); //如果在evict之前不執行flush,會有異常.