Hibernate悲观锁和乐观锁实例详解

本文研究的主要是Hibernate悲观锁和乐观锁的全部内容,具体介绍如下。

悲观锁

悲观锁通常是由数据库机制实现的,在整个过程中把数据锁住(查询时),只要事物不释放(提交/回滚),那么任何用户都不能查看或修改。

下面我们通过一个案例来说明。

案例:假设货物库存为1000,当核算员1取出了数据准备修改,但临时有事,就走了。期间核算员2取出了数据把数量减去200,然后核算员1回来了把刚才取出的数量减去200,这就出现了一个问题,核算员1并没有在800的基础上做修改。这就是所谓的更新丢失,采用悲观锁可以解决。

Inventory.java:

public class Inventory { 

  /* 存货编号 */
  private String itemNo;
  /* 存货名称 */
  private String itemName;
  /* 存货数量 */
  private int quantity; 

  //省略setter和getter方法
} 

Inventory.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="com.lixue.bean.Inventory" table="t_inventory">
    <!-- 主键手动分配 -->
    <id name="itemNo">
      <generator class="assigned"/>
    </id>
    <!-- 映射属性 -->
    <property name="itemName"/>
    <property name="quantity"/>
  </class>
</hibernate-mapping> 

测试类:

核算员1通过悲观锁的方式加载数据,并对数据进行修改!

public void testLoad1() {
    Session session = null;
    try {
      session = HibernateUtils.getSession();
      session.beginTransaction();
      /*在加载的时候就加上一把悲观锁,让其他用户都无法访问*/
      Inventory inv = (Inventory) session.load(Inventory.class, "1001", LockMode.UPGRADE);
      /*获取数据*/
      System.out.println("opt1-->itemNo=" + inv.getItemNo());
      System.out.println("opt1-->itemName=" + inv.getItemName());
      System.out.println("opt1-->quantity=" + inv.getQuantity());
      /*数量减去200*/
      inv.setQuantity(inv.getQuantity() - 200); 

      session.getTransaction().commit();
    } catch (Exception e) {
      e.printStackTrace();
      session.getTransaction().rollback();
    } finally {
      HibernateUtils.closeSession(session);
    }
  } 

核算员2和核算员1的操作相同,都是对数据库中的数据进行修改!

public void testLoad2() {
    Session session = null;
    try {
      session = HibernateUtils.getSession();
      session.beginTransaction();
      /*在加载数据的时候就加上一把锁,让其他人无法获取数据*/
      Inventory inv = (Inventory) session.load(Inventory.class, "1001", LockMode.UPGRADE);
      /*获取真实数据*/
      System.out.println("opt2-->itemNo=" + inv.getItemNo());
      System.out.println("opt2-->itemName=" + inv.getItemName());
      System.out.println("opt2-->quantity=" + inv.getQuantity());
      /*库存减去200*/
      inv.setQuantity(inv.getQuantity() - 200); 

      session.getTransaction().commit();
    } catch (Exception e) {
      e.printStackTrace();
      session.getTransaction().rollback();
    } finally {
      HibernateUtils.closeSession(session);
    }
  } 

注:两个核算员做的操作相同,如果加了悲观锁之后,核算员取出了数据并对数据进行修改,在核算员1没有提交事物之前,核算员2是不能对数据进行访问的,只能处于等待状态。知道核算员1把事物提交了之后,核算员2才有机会对数据库中的数据进行操作。

通过上面悲观锁的案例我们可以发现,悲观锁最大的好处就是可以防止更新丢失,当核算员1在处理数据的时候,核算员2只能处于等待状态,只有核算员1提交了事物之后,核算员2才有机会修改数据。但是也存在一个很大的问题,那就是,如果核算员1将数据查询出来后人就走掉了,那么其他人就得等上大半天,非常浪费时间,为了解决这个问题,我们可以使用乐观锁。

乐观锁

乐观锁并不是真正意义上的锁,大多数情况下是采用数据版本(version)的方式实现,一般在数据库中加入一个version字段,在读取数据的时候就将version读取出来,在保存数据的时候判断version的值是否小于数据库的version值,如果小于则不予更新,否则给予更新。

乐观锁下的javaBean设置,Inventory.java:

public class Inventory { 

  /*存货编号*/
  private String itemNo;
  /*存货名称*/
  private String itemName;
  /*存货数量*/
  private int quantity;
  /*数据版本*/
  private int version; 

  //省略setter和getter方法
} 

Inventory.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <!-- 在class标签中加上optimistc-lock属性,其值为版本信心 -->
  <class name="com.lixue.bean.Inventory" table="t_inventory" optimistic-lock="version">
    <!-- 主键映射 -->
    <id name="itemNo">
      <generator class="assigned"/>
    </id>
    <!-- 数据版本,必须在主键后面的位置 -->
    <version name="version"/>
    <!-- 基本属性映射 -->
    <property name="itemName"/>
    <property name="quantity"/>
  </class>
</hibernate-mapping> 

注:使用乐观锁的映射文件有规定即version字段的映射必须在主键ID之后第一个被映射。

测试:

核算员1在乐观锁的情况下处理数据:

public void testLoad1() {
    Session session = null;
    try {
      session = HibernateUtils.getSession();
      session.beginTransaction();
      /*乐观锁下加载数据*/
      Inventory inv = (Inventory)session.load(Inventory.class, "1001");
      /*实际获取数据*/
      System.out.println("opt1-->itemNo=" + inv.getItemNo());
      System.out.println("opt1-->itemName=" + inv.getItemName());
      System.out.println("opt1-->version=" + inv.getVersion());
      System.out.println("opt1-->quantity=" + inv.getQuantity());
      /*数量减去200*/
      inv.setQuantity(inv.getQuantity() - 200); 

      session.getTransaction().commit();
    }catch(Exception e) {
      e.printStackTrace();
      session.getTransaction().rollback();
    }finally {
      HibernateUtils.closeSession(session);
    }
  } 

核算员2在乐观锁的情况下处理数据(核算员2可以在核算员1未提交数据的前提下处理数据)

public void testLoad2() {
    Session session = null;
    try {
      session = HibernateUtils.getSession();
      session.beginTransaction();
      /*乐观锁下加载数据*/
      Inventory inv = (Inventory)session.load(Inventory.class, "1001");
      /*实际获取数据*/
      System.out.println("opt2-->itemNo=" + inv.getItemNo());
      System.out.println("opt2-->itemName=" + inv.getItemName());
      System.out.println("opt2-->version=" + inv.getVersion());
      System.out.println("opt2-->quantity=" + inv.getQuantity());
      /*数量减去200*/
      inv.setQuantity(inv.getQuantity() - 200); 

      session.getTransaction().commit();
    }catch(Exception e) {
      e.printStackTrace();
      session.getTransaction().rollback();
    }finally {
      HibernateUtils.closeSession(session);
    }
  }  

注:在核算员取出数据将数量减去200之后并未提交的前提下,核算员2也可以操作数据,这就有别于悲观锁,当核算员2操作了数据并且提交之后,数据库中数据版本version就会加1,那么当核算员1在回来进行事物提交时就会出现错误提示即数据已更新,请重新加载。

总结

悲观锁会影响高并发,所以用乐观锁比较好。

以上就是本文关于Hibernate悲观锁和乐观锁实例详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

您可能感兴趣的文章:

  • Hibernate实现悲观锁和乐观锁代码介绍
  • Java的Hibernate框架数据库操作中锁的使用和查询类型
(0)

相关推荐

  • Hibernate实现悲观锁和乐观锁代码介绍

    四种隔离机制不要忘记:(1,2,4,8) 1.read-uncommitted:能够去读那些没有提交的数据(允许脏读的存在) 2.read-committed:不会出现脏读,因为只有另一个事务提交才会读取来结果,但仍然会出现不可重复读和幻读现象. 4.repeatable read: MySQL 默认.可重复读,读数据读出来之后给它加把锁,其他人先别更新,等我用完了你再更新.你的事务没完,其他事务就不可能改这条记录. 8.serializable:序列化,最高级别.一个一个来,不去并发.效率最低

  • Java的Hibernate框架数据库操作中锁的使用和查询类型

     Hibernate与数据库锁 一.为什么要使用锁? 要想弄清楚锁机制存在的原因,首先要了解事务的概念. 事务是对数据库一系列相关的操作,它必须具备ACID特征: A(原子性):要么全部成功,要么全部撤销. C(一致性):要保持数据库的一致性. I(隔离性):不同事务操作相同数据时,要有各自的数据空间. D(持久性):一旦事务成功结束,它对数据库所做的更新必须永久保持. 我们常用的关系型数据库RDBMS实现了事务的这些特性.其中,原子性. 一致性和持久性都是采用日志来保证的.而隔离性就是由今天我

  • Hibernate悲观锁和乐观锁实例详解

    本文研究的主要是Hibernate悲观锁和乐观锁的全部内容,具体介绍如下. 悲观锁 悲观锁通常是由数据库机制实现的,在整个过程中把数据锁住(查询时),只要事物不释放(提交/回滚),那么任何用户都不能查看或修改. 下面我们通过一个案例来说明. 案例:假设货物库存为1000,当核算员1取出了数据准备修改,但临时有事,就走了.期间核算员2取出了数据把数量减去200,然后核算员1回来了把刚才取出的数量减去200,这就出现了一个问题,核算员1并没有在800的基础上做修改.这就是所谓的更新丢失,采用悲观锁可

  • 详解Java中的悲观锁与乐观锁

    一.悲观锁 悲观锁顾名思义是从悲观的角度去思考问题,解决问题.它总是会假设当前情况是最坏的情况,在每次去拿数据的时候,都会认为数据会被别人改变,因此在每次进行拿数据操作的时候都会加锁,如此一来,如果此时有别人也来拿这个数据的时候就会阻塞知道它拿到锁.在Java中,Synchronized和ReentrantLock等独占锁的实现机制就是基于悲观锁思想.在数据库中也经常用到这种锁机制,如行锁,表锁,读写锁等,都是在操作之前先上锁,保证共享资源只能给一个操作(一个线程)使用. 由于悲观锁的频繁加锁,

  • 基于Django的乐观锁与悲观锁解决订单并发问题详解

    前言 订单并发这个问题我想大家都是有一定认识的,这里我说一下我的一些浅见,我会尽可能的让大家了解如何解决这类问题. 在解释如何解决订单并发问题之前,需要先了解一下什么是数据库的事务.(我用的是mysql数据库,这里以mysql为例) 1)     事务概念 一组mysql语句,要么执行,要么全不不执行.  2)  mysql事务隔离级别 Read Committed(读取提交内容) 如果是Django2.0以下的版本,需要去修改到这个隔离级别,不然乐观锁操作时无法读取已经被修改的数据 Repea

  • Mybatis plus的自动填充与乐观锁的实例详解(springboot)

    自动填充 项目中经常会遇到一些数据,每次都使用相同的方式填充,如插入时间.更新时间.Mybatis-plus的自动填充功能可以帮助我们快速实现. 1.表中加入create_time,update_time字段 2.实体类注解填充字段 @TableField(fill= FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; /**

  • mysql 悲观锁与乐观锁的理解及应用分析

    本文实例讲述了mysql 悲观锁与乐观锁.分享给大家供大家参考,具体如下: 悲观锁与乐观锁是人们定义出来的概念,你可以理解为一种思想,是处理并发资源的常用手段. 不要把他们与mysql中提供的锁机制(表锁,行锁,排他锁,共享锁)混为一谈. 一.悲观锁 顾名思义,就是对于数据的处理持悲观态度,总认为会发生并发冲突,获取和修改数据时,别人会修改数据.所以在整个数据处理过程中,需要将数据锁定. 悲观锁的实现,通常依靠数据库提供的锁机制实现,比如mysql的排他锁,select .... for upd

  • Java中数据库常用的两把锁之乐观锁和悲观锁

    在写入数据库的时候需要有锁,比如同时写入数据库的时候会出现丢数据,那么就需要锁机制. 数据锁分为乐观锁和悲观锁,那么它们使用的场景如下: 1. 乐观锁适用于写少读多的情景,因为这种乐观锁相当于JAVA的CAS,所以多条数据同时过来的时候,不用等待,可以立即进行返回. 2. 悲观锁适用于写多读少的情景,这种情况也相当于JAVA的synchronized,reentrantLock等,大量数据过来的时候,只有一条数据可以被写入,其他的数据需要等待.执行完成后下一条数据可以继续. 他们实现的方式上有所

  • Mysql悲观锁和乐观锁的使用示例

    悲观锁 悲观锁,认为数据是悲观的.当我们查询数据的时候加上锁.防止其他线程篡改,直到对方拿到锁,才能修改. 比如,有如下的表.status=1表示可以下单,status=2表示不可以下订单.假如在并发的过程中有两个用户同时查到status=1,那么从逻辑上来说都可以去新增订单,但是会造成商品超卖. 如下例子 CREATE TABLE `goods` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL,

  • Java中的悲观锁与乐观锁是什么

    乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展.这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人. 悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程).传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁.Java中sy

  • MySQL中的悲观锁与乐观锁

    在关系型数据库中,悲观锁与乐观锁是解决资源并发场景的解决方案,接下来将详细讲解

随机推荐