• 不容易理解的 lock 和 merge


    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 已经被标记为过时了,可是为啥没有提示其替代方法呢?

  • 相关阅读:
    Topics
    telnetlib-telnet客户端操作
    logging-日志信息管理
    B.2 工具spy++
    B.1 XPath 获取技巧
    pyinstaller-将Python程序打包成一个独立可执行软件包
    探讨HTTP中敏感数据的安全性传输方案
    shell->一个经典的shell脚本结构
    c->再次封装已有函数的快速方法
    c->推荐的更安全的代码写法
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3356330.html
Copyright © 2020-2023  润新知