不明確的方法就不管了,指的就是transition persistent:A被persitent,導致A的關係人B也persistent,所以重點還是在怎麼讓A被persitent。
明確的方法有三種:update()、lock()與merge()。
update()
最熟悉的當就是update()了。
session.update(detachedObj);但是有一點要特別注意的,就是這個動作一定會觸發一次sql update,即使detached obj沒有被修改,原因在obj是detached,所以Hibernate無從得知是否被修改過了,所以只好統統update。
可以設定select-before-update="true"要求Hibernate先去select一次舊資料來比對決定是否要update,有點囉唆,但好處有二:
- 當obj很大時(就是column很多時,多少算多?五十個以上吧),可以避免無謂的update。
- 當upadte會觸發Hibernate interceptor或db trigger時,導致不想要的副作用時。
有點麻煩的方式,因為有個參數要設定LockMode。
session.lock(detachedObj, LockMode.NONE);
- LockMode.NONE:狀態從detached變成persistent,但是Hibernate不管這個detached obj有沒有被修改過,一律假設是乾淨未被修改過的,但是在lock之後的修改就會sync到db了,若lock之前有修改這時也會sync到db裡。
- 其他的LockMode就會檢查detached obj有沒有被修改過了。
精彩的來了!
session.merge(detachedObj);merge()主要用來對付一種討厭的情況或錯誤:org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [Book#10]。
程式大概都是長這樣:
session = sf.openSession(); tx = session.beginTransaction(); List list = session.createCriteria(Book.class).list(); // 給你掛掉 session.update(aBook); tx.commit();aBook是detached obj,而list中也包括了與aBook相同id的另一個物件,所以在list之後,相同id的物件已經存在session cache中了,這時要把aBook再persistent到session裡就會掛掉,因為同一id的物件在同一session裡只能存有一份。
而一般在update detached obj之前會把同id的原物件取出來有幾種可能:做log或做欄位正確性檢查,因此在update之前執行get/list就無法避免了。
之前的作法就是用session.evict()將新取出來的物件踢出session,然後再update,但是這樣有個缺點,就是evict的動作得發生在business layer,造成business與dao的coupling。
session = sf.openSession(); tx = session.beginTransaction(); List list = session.createCriteria(Book.class).list(); // 麻煩的作法 session.evict(list); // 沒事了 session.update(aBook); tx.commit();現在救星來了,改用merge()吧。
session = sf.openSession(); tx = session.beginTransaction(); List list = session.createCriteria(Book.class).list(); // 安全過關 aBook = session.merge(aBook); tx.commit(); // 這個要特別注意 return aBook;merge時,Hibernate會將detached obj的資料都copy到persistent obj裡,然後回傳persistent obj,所以detached obj還是detached。
所以回傳的那一筆跟原本在session cache裡那一筆是同一筆,而被merge的那一筆跟回傳的與原本在session cache裡那兩筆還是不同的。
被merge的 != 原本在session cache裡那一筆 被merge的 != 回傳的那一筆 原本在session cache裡那一筆 == 回傳的那一筆例外狀況:
- 當session cache裡沒有相同id的物件時,去db裡面找。
- 當db裡找不到時,就新增一筆新的。
- 當被merge的obj不是detached而是transient時,就新增一筆新的。
沒有留言:
張貼留言