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);
根据注释可以知道其有三个职责:
- 执行乐观锁检查,然后执行。
- 提升为悲观锁,然后执行。
- 将一个透明的实例(脱钩)和 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 已经被标记为过时了,可是为啥没有提示其替代方法呢?