【Hibernate】Hibernate对“悲观”和“乐观”锁的支持

来源:互联网 发布:赢时胜软件上海分公司 编辑:程序博客网 时间:2024/06/10 01:16

          首先,“锁”这个东西,可以认为是一种思想,悲观锁还是乐观锁,是人定义出来的一种概念,并非理解为DBMS的专属。换个称呼,叫做“悲观并发控制”或者“乐观并发控制”更便于我们理解二者的意义。

        

一、概念

       ----(该部分内容来源于网络:http://www.open-open.com/lib/view/open1452046967245.html)----

       悲观锁:

       在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作都某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。
       悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度(悲观),因此,在整个数据处理过程中,将数据处于锁定状态。 悲观锁的实现,往往依靠数据库提供的锁机制 (也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)


       乐观锁:

       在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。乐观事务控制最早是由孔祥重(H.T.Kung)教授提出。
       乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。
       相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。

           ---------------------------------------------------------------------------------------------------


二、实例分析

        如图所示:

        

          两个人分别从1000里取出200,A本该剩余800,B本该剩余600,观察上图,A剩余了800,同时B也剩余了800。问题就出在A取出的同时,B也去取,导致被减数读错。

               

       如图所示,此时方为正解,既然了解了悲观锁和乐观锁的特性,就分别来实现他们。


三、代码实现

      一、悲观锁

    (1)实体类

public class Inventory {//编号private String itemNo;//名称private String itemName;//数量private int quantity;public String getItemName() {return itemName;}public void setItemName(String itemName) {this.itemName = itemName;}public String getItemNo() {return itemNo;}public void setItemNo(String itemNo) {this.itemNo = itemNo;}public int getQuantity() {return quantity;}public void setQuantity(int quantity) {this.quantity = quantity;}}

     (2)单元测试

       测试方法一:模拟操作员A取奶粉

public void testLoad1() {Session session = null;try {session = HibernateUtils.getSession();session.beginTransaction();//使用锁的模式为“悲观锁” Inventory inv = (Inventory)session.load(Inventory.class, "1001", LockMode.UPGRADE); //使用LockMode.UPGRADE,悲观锁的应用类型System.out.println("opt1-->itemNo=" + inv.getItemNo());System.out.println("opt1-->itemName=" + inv.getItemName());System.out.println("opt1-->quantity=" + inv.getQuantity());inv.setQuantity(inv.getQuantity() - 200);session.getTransaction().commit();}catch(Exception e) {e.printStackTrace();session.getTransaction().rollback();}finally {HibernateUtils.closeSession(session);}}

      测试方法二:模拟B操作员取奶粉

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());inv.setQuantity(inv.getQuantity() - 200);session.getTransaction().commit();}catch(Exception e) {e.printStackTrace();session.getTransaction().rollback();}finally {HibernateUtils.closeSession(session);}}
       通过在加载Inventory的Load方法中使用:LockMode.UPGRADE,标注加载过程执行了“悲观锁”,使得在执行testLoad1的时候,锁定了“1001”行记录(因为在select * for update中,锁级别是“行级锁”),只有在testLoad1()的事务提交之后,才进行testLoad2()的执行。

       至此,保证了A和B分别能够拿到“800”和“600”的执行结果。


       二、乐观锁

      (1)实体类,同上

      (2)在hbm.xml配置文件中(hibernate的映射文件)

<class name="" table="" optimistic-lock="version">
       加入对乐观锁的属性“optimistic-lock”,并绑定“version”字段。

      (3)单元测试

       测试方法一:模拟A的取奶粉操作

public void testLoad1() {Session session = null;try {session = HibernateUtils.getSession();session.beginTransaction();Inventory inv = (Inventory)session.load(Inventory.class, "1001"); //去掉了optimistic-lock属性配置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());inv.setQuantity(inv.getQuantity() - 200);  //设置金额减少session.getTransaction().commit();}catch(Exception e) {e.printStackTrace();session.getTransaction().rollback();}finally {HibernateUtils.closeSession(session);}}
         测试方法二:模拟B的取奶粉操作

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());inv.setQuantity(inv.getQuantity() - 200);session.getTransaction().commit();}catch(Exception e) {e.printStackTrace();session.getTransaction().rollback();}finally {HibernateUtils.closeSession(session);}}

          对比“悲观锁”和“乐观锁”,于Hibernate的实现,load方法加载过程不一样,前者通过LockMode.UPGRADE加以限制,后者则是在hbm.xml中,通过class标签加以限制。

      初次接触Hibernate对并发的控制,不足之处,请多多指教!


       




0 0
原创粉丝点击