乐观锁的作用


乐观锁的作用

乐观锁的主要作用是为了解决事务并发带来的问题。相对于悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。

悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本(Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个"version"字段来实现。

乐观锁的工作原理

读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

基于hibernate的乐观锁实现

基于hibernate的乐观锁实现一般有以下两种方式

  • 基于version
  • 基于timestamp


这里就介绍一下基于version的乐观锁实现

Xml代码 复制代码  收藏代码
  1. <?xml version="1.0"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC  
  3.         "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  4.         "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  5. <hibernate-mapping>  
  6.     <class name="test.Dir" table="t_dir">  
  7.         <id name="id" type="string" unsaved-value="null">  
  8.          <column name="id_" sql-type="char(32)" not-null="true" />  
  9.          <generator class="uuid.hex" />  
  10.     </id>  
  11.         <version column="version_" name="version" />  
  12.         <property name="name" column="name_" type="string"/>  
  13.         <property name="size" column="size_" type="long" />  
  14.         <many-to-one name="dir" column="pid_" class="test.Dir" />  
  15.     </class>  
  16. </hibernate-mapping>  


这里注意version的位置,一定是要放置在id的后面

由乐观锁引发的问题

当两个不同的事务同时读取到一条数据并进行修改时,这个时候程序就会抛出org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)异常。

这里同样有两种情况

一种是两个不同的事务的情况

Java代码 复制代码  收藏代码
  1. @Test  
  2. public void testTransation1(){  
  3.     Session session1 = null;  
  4.     Session session2 = null;  
  5.     try{  
  6.         session1 = HibernateUtil.getSession();  
  7.         session2 = HibernateUtil.getSession();  
  8.         Dir dir1 = (Dir)session1.load(Dir.class"4028811a3d3387d3013d3387d4a40008");  
  9.         Dir dir2 = (Dir)session2.load(Dir.class"4028811a3d3387d3013d3387d4a40008");  
  10.         Transaction tx1 = session1.beginTransaction();  
  11.         dir1.setSize(1111);  
  12.         tx1.commit();  
  13.         Transaction tx2 = session2.beginTransaction();  
  14.         dir2.setSize(999);  
  15.         tx2.commit();  
  16.         System.out.println("事务2提交");  
  17.     }catch(Exception e){  
  18.         e.printStackTrace();  
  19.     }finally{  
  20.         if(session1 != null){  
  21.             session1.close();  
  22.         }  
  23.         if(session2 != null){  
  24.             session2.close();  
  25.         }  
  26.     }  
  27. }  
	@Test
	public void testTransation1(){
		Session session1 = null;
		Session session2 = null;
		try{
			session1 = HibernateUtil.getSession();
			session2 = HibernateUtil.getSession();
			Dir dir1 = (Dir)session1.load(Dir.class, "4028811a3d3387d3013d3387d4a40008");
			Dir dir2 = (Dir)session2.load(Dir.class, "4028811a3d3387d3013d3387d4a40008");
			Transaction tx1 = session1.beginTransaction();
			dir1.setSize(1111);
			tx1.commit();
			Transaction tx2 = session2.beginTransaction();
			dir2.setSize(999);
			tx2.commit();
			System.out.println("事务2提交");
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(session1 != null){
				session1.close();
			}
			if(session2 != null){
				session2.close();
			}
		}
	}


事务2提交时发现version的值不一样,这个时候就会抛出org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)异常


第二种情况是子事务的情况
Java代码 复制代码  收藏代码
  1. @Test  
  2. public void testTransation5(){  
  3.     Session session1 = null;  
  4.     Session session2 = null;  
  5.     try{  
  6.         session1 = HibernateUtil.getSession();  
  7.         session2 = HibernateUtil.getSession();  
  8.         Dir dir1 = (Dir)session1.load(Dir.class"4028811a3d3387d3013d3387d4a40008");  
  9.         Dir dir2 = (Dir)session2.load(Dir.class"4028811a3d3387d3013d3387d4a40008");  
  10.         Transaction tx1 = session1.beginTransaction();  
  11.         Transaction tx2 = session2.beginTransaction();  
  12.         dir2.setSize(7777);  
  13.         tx2.commit();  
  14.         dir1.setSize(3333);  
  15.         tx1.commit();  
  16.     }catch(Exception e){  
  17.         e.printStackTrace();  
  18.     }finally{  
  19.         if(session1 != null){  
  20.             session1.close();  
  21.         }  
  22.         if(session2 != null){  
  23.             session2.close();  
  24.         }  
  25.     }  
  26. }  
	@Test
	public void testTransation5(){
		Session session1 = null;
		Session session2 = null;
		try{
			session1 = HibernateUtil.getSession();
			session2 = HibernateUtil.getSession();
			Dir dir1 = (Dir)session1.load(Dir.class, "4028811a3d3387d3013d3387d4a40008");
			Dir dir2 = (Dir)session2.load(Dir.class, "4028811a3d3387d3013d3387d4a40008");
			Transaction tx1 = session1.beginTransaction();
			Transaction tx2 = session2.beginTransaction();
			dir2.setSize(7777);
			tx2.commit();
			dir1.setSize(3333);
			tx1.commit();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(session1 != null){
				session1.close();
			}
			if(session2 != null){
				session2.close();
			}
		}
	}


我们发现事物2被包裹在事务1里面,如果Dir被配置为延迟加载(hibnernate默认就是延迟加载的)的,这个时候在事务1进行提交的时候,会先去数据库进行查询一下,再进行更新操作。

如果Dir被配置为非延迟加载(lazy="false")的,这个时候事务1在提交的时候就不会先去查询数据库,而是直接提交,在提交的时候发现version不匹配,因而也会抛出org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)异常

解决办法

1、捕获StaleObjectStateException异常,提示数据过时已被修改,让用户重新提交

2、尽量从业务方面去减小事务块,事务块越大,由乐观锁引起的问题的概率就越大
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值