hibernate的各種保存方式的區別

===========================
hibernate的update和merge
    首先, merge作用大致相當於saveorupdate這個方法, 即有唯一性標識, 則運行save, 沒有則運行update

    下面來比較2者的區別, update(object), 方法運行後, object是持久化狀態, 而Object obj = merge(object), 方法運行後, object是脫管狀態, obj是持久化狀態

    最後比較2者的性能, 本人使用for循環測試10000次, merge: 150ms, update: 50ms, 顯然update性能佔優勢.

    所以, merge的用法應該是在需要返回被保存的對象時使用.
================================

一、預備知識:
在所有之前,說明一下,對於hibernate,它的對象有三種狀態,transient、persistent、detached
下邊是常見的翻譯辦法:
transient:瞬態或者自由態
persistent:持久化狀態
detached:脫管狀態或者遊離態

脫管狀態的實例可以通過調用save()、persist()或者saveOrUpdate()方法進行持久化。

持久化實例可以通過調用 delete()變成脫管狀態。通過get()或load()方法得到的實例都是持久化狀態的。

脫管狀態的實例可以通過調用 update()、0saveOrUpdate()、lock()或者replicate()進行持久化。

(1)當通過get或load方法得到的po對象它們都處於persistent,但如果執行delete(po)時(但不能執行事務),該po狀態就處於detached, (表示和session脫離關聯),因delete而變成遊離態可以通過save或saveOrUpdate()變成持久態

(2)當把session關閉時,session緩存中的persistent的po對象也變成detached
因關閉session而變成遊離態的可以通過lock、save、update變成持久態
持久態實例可以通過調用 delete()變成脫管狀態。
通過get()或load()方法得到的實例都是持久化狀態的。
脫管狀態的實例可以通過調用lock()或者replicate()進行持久化。



遊離或者自由狀態下的實例可以通過調用merge()方法成爲一個新的持久化實例。


save()和persist()將會引發SQL的INSERT,delete()會引發SQL的DELETE,
而update()或merge()會引發SQL的UPDATE。對持久化(persistent)實例的修改在刷新提交的時候會被檢測到,它也會引起SQLUPDATE。saveOrUpdate()或者replicate()會引發SQLINSERT或者UPDATE

二、save 和update區別
把這一對放在第一位的原因是因爲這一對是最常用的。
save的作用是把一個新的對象保存
update是把一個脫管狀態的對象保存

三、update 和saveOrUpdate區別
這個是比較好理解的,顧名思義,saveOrUpdate基本上就是合成了save和update
引用hibernate reference中的一段話來解釋他們的使用場合和區別
通常下面的場景會使用update()或saveOrUpdate():
程序在第一個session中加載對象
該對象被傳遞到表現層
對象發生了一些改動
該對象被返回到業務邏輯層
程序調用第二個session的update()方法持久這些改動

saveOrUpdate()做下面的事:
如果對象已經在本session中持久化了,不做任何事
如果另一個與本session關聯的對象擁有相同的持久化標識(identifier),拋出一個異常如下:

org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [org.itfuture.www.po.Xtyhb#5]

如果對象沒有持久化標識(identifier)屬性,對其調用save()
如果對象的持久標識(identifier)表明其是一個新實例化的對象,對其調用save()
如果對象是附帶版本信息的(通過<version>或<timestamp>) 並且版本屬性的值表明其是一個新實例化的對象,save()它。
否則update() 這個對象

四、persist和save區別
這個是最迷離的一對,表面上看起來使用哪個都行,在hibernate reference文檔中也沒有明確的區分他們.
這裏給出一個明確的區分。(可以跟進src看一下,雖然實現步驟類似,但是還是有細微的差別)
這裏參考http://opensource.atlassian.com/projects/hibernate/browse/HHH-1682中的一個說明:
---------------------------------------------------------------------------------
I found that a lot of people have the same doubt. To help to solve this issue
I'm quoting Christian Bauer:
"In case anybody finds this thread...

persist() is well defined. It makes a transient instance persistent. However,
it doesn't guarantee that the identifier value will be assigned to the persistent
instance immediately, the assignment might happen at flush time. The spec doesn't say
that, which is the problem I have with persist().

persist() also guarantees that it will not execute an INSERT statement if it is
called outside of transaction boundaries. This is useful in long-running conversations
with an extended Session/persistence context.A method like persist() is required.

save() does not guarantee the same, it returns an identifier, and if an INSERT
has to be executed to get the identifier (e.g. "identity" generator, not "sequence"),
this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is not good in a long-running conversation with an extended Session/persistence context."

---------------------------------------------------------------------------------
簡單翻譯一下上邊的句子的主要內容:
1,persist把一個瞬態的實例持久化,但是並"不保證"標識符被立刻填入到持久化實例中,標識符的填入可能被推遲
到flush的時間。

2,persist"保證",當它在一個transaction外部被調用的時候並不觸發一個Sql Insert,這個功能是很有用的,當我們通過繼承Session/persistence context來封裝一個長會話流程的時候,一個persist這樣的函數是需要的。

3,save"不保證"第2條,它要返回標識符,所以它會立即執行Sql insert,不管是不是在transaction內部還是外部


五、saveOrUpdateCopy,merge和update區別
首先說明merge是用來代替saveOrUpdateCopy的,這個詳細見這裏
http://www.blogjava.net/dreamstone/archive/2007/07/28/133053.html
然後比較update和merge
update的作用上邊說了,這裏說一下merge的
如果session中存在相同持久化標識(identifier)的實例,用用戶給出的對象的狀態覆蓋舊有的持久實例
如果session沒有相應的持久實例,則嘗試從數據庫中加載,或創建新的持久化實例,最後返回該持久實例
用戶給出的這個對象沒有被關聯到session上,它依舊是脫管的

重點是最後一句:

當我們使用update的時候,執行完成後,我們提供的對象A的狀態變成持久化狀態

但當我們使用merge的時候,執行完成,我們提供的對象A還是脫管狀態,hibernate或者new了一個B,或者檢索到一個持久對象B,並把我們提供的對象A的所有的值拷貝到這個B,執行完成後B是持久狀態,而我們提供的A還是託管狀態

六、flush和update區別
這兩個的區別好理解
update操作的是在脫管狀態的對象
而flush是操作的在持久狀態的對象。
默認情況下,一個持久狀態的對象是不需要update的,只要你更改了對象的值,等待hibernate flush就自動
保存到數據庫了。hibernate flush發生再幾種情況下:
1,調用某些查詢的時候
2,transaction commit的時候
3,手動調用flush的時候

七、lock和update區別
update是把一個已經更改過的脫管狀態的對象變成持久狀態
lock是把一個沒有更改過的脫管狀態的對象變成持久狀態(針對的是因Session的關閉而處於脫管狀態的po對象,且不能針對因delete而處於脫管狀態的po對象) 


對應更改一個記錄的內容,兩個的操作不同: 
(1)update的操作步驟是:更改脫管的對象->調用update 
(2)lock的操作步驟是:調用lock把未修改的對象從脫管狀態-->更改持久狀態的對象的內容-->等待flush或者手動flush

參考內容:
http://www.blogjava.net/iamtin/archive/2006/03/06/33910.aspx
http://opensource.atlassian.com/projects/hibernate/browse/HHH-1682
http://www.redsaga.com/hibernate-ref/3.x/zh-cn/html/objectstate.html

http://www.blogjava.net/dreamstone/archive/2007/07/29/133071.html

八、clear和evcit的區別
clear完整的清除session緩存
evcit(obj)把某個持久化對象從session的緩存中清空。
 
session.lock(xtyhb,LockMode.NONE);//表示直接到緩存中去找變成持久態的對象
session.lock(xtyhb,LockMode.READ);//先通過ID讀數據庫該記錄的ID看是否有該記錄,如果有接着到緩存中去找變成持久態的對象

 

九、Hibernate 中的attachDirty,attachClean,merge之間的區別?
merge:將傳入的detached狀態的對象的屬性複製到持久化對象中,並返回該持久化對象 。假如該session中沒有關聯的持久化對象,加載一個,假如傳入對象未保存,保存一個副本並作爲持久對象返回,傳入對象依然保持detached狀態。

attachDirty:將傳入的對象持久化並保存。假如對象未保存(Transient狀態),調用save方法保存。假如對象已保存(Detached狀態),調用update方法將對象與Session重新關聯。

attachClean:將傳入的對象狀態設置爲Transient狀態。

十、load,get
(1)當記錄不存在時候,get方法返回null,load方法產生異常
(2)load方法可以返回實體的代理類,get方法則返回真是的實體類
(3)load方法可以充分利用hibernate的內部緩存和二級緩存中的現有數據,而get方法僅僅在內部緩存中進行數據查找,如果沒有發現數據則將越過二級緩存,直接調用SQL查詢數據庫。
   (4) 也許別人把數據庫中的數據修改了,load如何在緩存中找到了數據,則不會再訪問數據庫,而get則會返回最新數據。

十一、find,iterator 
     (1) iterator首先會獲取符合條件的記錄的id,再跟據id在本地緩存中查找數據,查找不到的再在數據庫中查找,結果再存在緩存中。N+1條SQL。
 (2)find跟據生成的sql語句,直接訪問數據庫,查到的數據存在緩存中,一條sql。

十二、fetch,join
fetch參數指定了關聯對象抓取的方式是select查詢還是join查詢,

select方式時先查詢返回要查詢的主體對象(列表),再根據關聯外鍵id,每一個對象發一個select查詢,獲取關聯的對象,形成n+1次查詢;

而join方式,主體對象和關聯對象用一句外鍵關聯的sql同時查詢出來,不會形成多次查詢。
如果你的關聯對象是延遲加載的,它當然不會去查詢關聯對象。
另外,在hql查詢中配置文件中設置的join方式是不起作用的(而在所有其他查詢方式如get、criteria或再關聯獲取等等都是有效的),會使用select方式,除非你在hql中指定join fetch某個關聯對象。
http://www.iteye.com/topic/77475

十三、findByExample 的用法
org.hibernate.criterion.Example類允許你通過一個給定實例構建一個條件查詢。
此實例的屬性值將做成查詢條件。

Cat cat = new Cat();
cat.setSex('F');
cat.setColor(Color.BLACK);
List results = session.createCriteria(Cat.class)
  .add( Example.create(cat))
  .list();

Example.create(cat) 表示根據cat這個對象來構造一個查詢條件。
上面這條語句將查詢sex 爲 F 而且Color爲BLACK的所有Cat記錄。

版本屬性、標識符和關聯被忽略。默認情況下值爲null的屬性將被排除。
可以自行調整Example使之更實用。

Example example = Example.create(cat)
  .excludeZeroes()       //排除值爲0的屬性
  .excludeProperty("color") //排除 color屬性
  .ignoreCase()         //忽略大小寫
  .enableLike();         //啓用模糊查詢
List results = session.createCriteria(Cat.class)
  .add(example)
  .list();

甚至可以使用examples在關聯對象上放置條件。

List results = session.createCriteria(Cat.class)
  .add( Example.create(cat) )
  .createCriteria("mate")
    .add( Example.create( cat.getMate() ) )
  .list();

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章