Hibernate:不容易理解的 lock 和 merge


背景

lock 和 merge 在字面上很容易理解它們的語義,不過它們的實際行為所代表的語義范圍要大一點,本文就簡單的記錄下來,還請朋友們多批評和指正。

Lock

官方的注釋

 1     /**
 2      * Obtain the specified lock level upon the given object. This may be used to
 3      * perform a version check (<tt>LockMode.READ</tt>), to upgrade to a pessimistic
 4      * lock (<tt>LockMode.PESSIMISTIC_WRITE</tt>), or to simply reassociate a transient instance
 5      * with a session (<tt>LockMode.NONE</tt>). This operation cascades to associated
 6      * instances if the association is mapped with <tt>cascade="lock"</tt>.
 7      *
 8      * @param object a persistent or transient instance
 9      * @param lockMode the lock level
10      *
11      * @deprecated instead call buildLockRequest(LockMode).lock(object)
12      */
13     @Deprecated
14     public void lock(Object object, LockMode lockMode);

根據注釋可以知道其有三個職責:

  1. 執行樂觀鎖檢查,然后執行。
  2. 提升為悲觀鎖,然后執行。
  3. 將一個透明的實例(脫鈎)和 Session 進行關聯。

注意:1 和 2 都會執行 3。

LockMode.NONE

測試代碼

 1 package demo;
 2 
 3 import model.*;
 4 import org.hibernate.*;
 5 
 6 /*
 7  * lock 會將處於 transparent 狀態的對象變為 persisted。
 8  */
 9 public class LockDemo implements Demo {
10 
11     @Override
12     public void run() {
13         SessionHelper.read(new SessionAction() {
14             User user = UserHelper.createUser();
15 
16             @SuppressWarnings("deprecation")
17             @Override
18             public void action(Session session) {
19                 session.lock(user, LockMode.NONE);
20                 
21                 // 為了測試執行 lock 后實例是否變為持久化狀態。
22                 user = (User) session.get(User.class, user.getId());
23             }
24 
25         });
26     }
27 }

說明:上例執行后沒有任何 SQL 輸出。

LockMode.READ

測試代碼

 1 package demo;
 2 
 3 import model.*;
 4 import org.hibernate.*;
 5 
 6 /*
 7  * lock 會將處於 transparent 狀態的對象變為 persisted。
 8  */
 9 public class LockDemo implements Demo {
10 
11     @Override
12     public void run() {
13         SessionHelper.read(new SessionAction() {
14             User user = UserHelper.createUser();
15 
16             @SuppressWarnings("deprecation")
17             @Override
18             public void action(Session session) {
19                 session.lock(user, LockMode.READ);
20                 
21                 // 為了測試執行 lock 后實例是否變為持久化狀態。
22                 user = (User) session.get(User.class, user.getId());
23             }
24 
25         });
26     }
27 }

輸出結果

1     /* READ lock model.User */ select
2         Id 
3     from
4         Users 
5     where
6         Id =? 
7         and Version =?

說明:上例執行了樂觀鎖檢查,我還沒有測試檢查失敗的場景,估計是會拋出異常。

LockMode.UPGRADE

測試代碼

 1 package demo;
 2 
 3 import model.*;
 4 import org.hibernate.*;
 5 
 6 /*
 7  * lock 會將處於 transparent 狀態的對象變為 persisted。
 8  */
 9 public class LockDemo implements Demo {
10 
11     @Override
12     public void run() {
13         SessionHelper.read(new SessionAction() {
14             User user = UserHelper.createUser();
15 
16             @SuppressWarnings("deprecation")
17             @Override
18             public void action(Session session) {
19                 session.lock(user, LockMode.UPGRADE);
20                 
21                 // 為了測試執行 lock 后實例是否變為持久化狀態。
22                 user = (User) session.get(User.class, user.getId());
23             }
24 
25         });
26     }
27 }

輸出結果

1     /* UPGRADE lock model.User */ select
2         Id 
3     from
4         Users 
5     where
6         Id =? 
7         and Version =? for update

說明:上例將對象對應的數據庫記錄升級為悲觀鎖,由此可以保證修改的串行化。

Merge

官方注釋

 1     /**
 2      * Copy the state of the given object onto the persistent object with the same
 3      * identifier. If there is no persistent instance currently associated with
 4      * the session, it will be loaded. Return the persistent instance. If the
 5      * given instance is unsaved, save a copy of and return it as a newly persistent
 6      * instance. The given instance does not become associated with the session.
 7      * This operation cascades to associated instances if the association is mapped
 8      * with {@code cascade="merge"}
 9      * <p/>
10      * The semantics of this method are defined by JSR-220.
11      *
12      * @param object a detached instance with state to be copied
13      *
14      * @return an updated persistent instance
15      */
16     public Object merge(Object object);

根據注釋可以知道 merge 有兩個職責:

  1. 如果對象為 unsaved,對對象的拷貝執行 save 方法,返回拷貝的對象。
  2. 如果對象為 detached,將對象的狀態拷貝到和對象的標識一樣的持久化對象中,如果持久化對象不存在,就執行 get 方法將其加載。

detached 對象測試

測試代碼

 1 package demo;
 2 
 3 import model.*;
 4 import org.hibernate.*;
 5 
 6 /*
 7  * merge 不會將參數變為持久化狀態,而是使用參數修改 session 中的持久化對象,如果 session 中不包含持久化
 8  * 對象,就從數據庫中加載一個,如果對象為 unsaved 狀態,就對其拷貝執行 save。
 9  */
10 public class MergeDemo implements Demo {
11 
12     @Override
13     public void run() {
14         SessionHelper.execute(new SessionAction() {
15             User user = UserHelper.createUser();
16 
17             @Override
18             public void action(Session session) {
19                 User newUser = new User();
20                 newUser.setId(user.getId());
21                 newUser.setUsername("shijiucha");
22                 newUser.setPassword(user.getPassword());
23                 newUser.setVersion(user.getVersion());
24 
25                 session.merge(newUser);
26             }
27 
28         });
29     }
30 }

輸出結果

 1 begin transaction
 2 action
 3 Hibernate: 
 4     /* load model.User */ select
 5         user0_.Id as Id1_2_0_,
 6         user0_.Version as Version2_2_0_,
 7         user0_.Username as Username3_2_0_,
 8         user0_.Password as Password4_2_0_ 
 9     from
10         Users user0_ 
11     where
12         user0_.Id=?
13 flush and commit
14 Hibernate: 
15     /* update
16         model.User */ update
17             Users 
18         set
19             Version=?,
20             Username=?,
21             Password=? 
22         where
23             Id=? 
24             and Version=?

說明:上例先執行了 select 語句,然后執行了合並過程,因為有修改,在 flush 的時候產生了 update 語句。

unsaved 對象測試

測試代碼

 1 package demo;
 2 
 3 import model.*;
 4 import org.hibernate.*;
 5 
 6 /*
 7  * merge 不會將參數變為持久化狀態,而是使用參數修改 session 中的持久化對象,如果 session 中不包含持久化
 8  * 對象,就從數據庫中加載一個,如果對象為 unsaved 狀態,就對其拷貝執行 save。
 9  */
10 public class MergeDemo implements Demo {
11 
12     @Override
13     public void run() {
14         SessionHelper.execute(new SessionAction() {
15             User user = UserHelper.createUser();
16 
17             @Override
18             public void action(Session session) {
19                 User newUser = new User();
20                 newUser.setId(user.getId());
21                 newUser.setUsername("shijiucha");
22                 newUser.setPassword(user.getPassword());
23                 //newUser.setVersion(user.getVersion());
24 
25                 session.merge(newUser);
26             }
27 
28         });
29     }
30 }

輸出結果

 1 begin transaction
 2 action
 3 Hibernate: 
 4     /* insert model.User
 5         */ insert 
 6         into
 7             Users
 8             (Version, Username, Password) 
 9         values
10             (?, ?, ?)
11 flush and commit

說明:上例只執行了 insert 語句,因為 user 是 unsaved 狀態。

備注

hibernate 的注釋寫的真是漂亮。

另外說一句:lock 已經被標記為過時了,可是為啥沒有提示其替代方法呢?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM