背景
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);
根據注釋可以知道其有三個職責:
- 執行樂觀鎖檢查,然后執行。
- 提升為悲觀鎖,然后執行。
- 將一個透明的實例(脫鈎)和 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 有兩個職責:
- 如果對象為 unsaved,對對象的拷貝執行 save 方法,返回拷貝的對象。
- 如果對象為 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 已經被標記為過時了,可是為啥沒有提示其替代方法呢?