`
bjlf1105
  • 浏览: 24760 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Hibernate悲观锁与乐观锁

阅读更多

 

假如有两个客户端或者说是两个事务同时去修改同一条数据,就会产生并发的现象,这时候有数据的更新丢失。现在就来模拟下这个并发现象的例子:

客户端 1 或者说是事务 1

session.beginTransaction();
Person p = (Person)session.load(Person.class, 1);
//Person p = (Person)session.load(Person.class, 1 ,LockOptions.UPGRADE);
p.setName("士兵乙");
session.getTransaction().commit();

 客户端 2 或者说是事务 2

session.beginTransaction();
Person p = (Person)session.load(Person.class, 1);
//Person p = (Person)session.load(Person.class, 1 ,LockOptions.UPGRADE);
p.setAddress("桃花岛");
session.getTransaction().commit();

 

假设现在有两个同时运行的事务:事务 1 和事务 2 两个事务,事务 1 运行到 setName 这行代码了并 setName 了修改了这个值,但是事务 1 还没有提交,这时候事务 2 也运行到了 setAddress 这行代码,但还没有执行,这时候,事务 1 提交了,巧的是事务 2 在事务 1 提交后,事务 2 它也提交了,这时候事务 1 更新的 setName 就丢失了,在显示中的客户端 1 看到自己并没有更新,还是原来的那个 Name ,而事务 2 却成功的更新了。这样前一个更新发生了丢失,即前一个事务发生了丢失。怎么解决这种并发现象呢?

1、  悲观锁定,很简单,就是在第一个客户端在加载这个对象的时候,利用数据库的能力,锁定这条记录,然后第二个客户端想要加载它改它,不让他改,直到第一个事务结束了,这时第二个客户端你才能加载修改它,即这个加载有先后,这样就控制了更新数据的丢失

Hibernate 是这样做的:

事务1:
session.beginTransaction();
Person p = (Person)session.load(Person.class, 1 ,LockOptions.UPGRADE);
Hibernate: select per0_.id as id0_0_, per0_.versionNumber as versionN2_0_0_, 
per0_.name as name0_0_, per0_.address as address0_0_, per0_.qq as qq0_0_, 
per0_.groupId as groupId0_0_ from t_person per0_ where per0_.id=? for 
update

 发出了一条数据库级别锁定一条记录的 for update 语句,那么在事务提交之前这条记录都被锁定,因此下面第二个客户端,他上来也想修改,可是他改不了,它就停在加载对象这行代码上,不会继续往下执行,因为他在等第一个客户端提交后,他才能加载这个对象,然后修改它,这样就保证了事务的一致性

p.setName("士兵乙");
session.getTransaction().commit();
事务2:
session.beginTransaction();
Person p = (Person)session.load(Person.class, 1 ,LockOptions.UPGRADE);
也发出一条跟上面一样的for update的sql语句,但是这是的name已经是上面第一个
事务修改后的name了,这时事务再进行更新就不会有什么因并发访问而发生的更新丢
失了。
p.setAddress("桃花岛");
session.getTransaction().commit();

 但是悲观锁定是针对这条记录的锁定所以并发访问比较大的话,那么会导致性能下降,一般不推荐使用

 

2、乐观锁定是用 Hibernate 来锁定它,而不是利用数据库的能力来维护,这个主要是通过版本控制来维护事务的。在实体中配置这个 version 标识,它必须定义在映射文件中的 id property 之间,每次更新,这个 version 都会发生改变。这时候如果有两个事务同时修改这条记录,一开始他们的版本字段都是 0 ,接着只要一个事务更新了这条数据,那么这个版本字段加 1 ,数据库也加 1 ,当第二个事务也要更新这条记录,这时候 Hibernate 会对比这个版本字段,发现版本不一致,一个是 0 ,一个是 1 ,接着弹出友好的提示,或抛出异常,那么你再加载一遍或 refresh 以下,这样就保证了数据的安全更新,显然这样提高了并发的性能。

<id name="id">
	<generator class="native"/>
</id>
<version name="versionNumber"></version>
<property name="name"/>
 
事务1:
session.beginTransaction();
Person p = (Person)session.load(Person.class, 1);
p.setName("路人丙");
session.getTransaction().commit();
Hibernate: select per0_.id as id0_0_, per0_.versionNumber as 
versionN2_0_0_, per0_.name as name0_0_, per0_.address as address0_0_,
 per0_.qq as qq0_0_, per0_.groupId as groupId0_0_ from t_person per0_ 
where per0_.id=?
Hibernate: update t_person set versionNumber=?, name=?, address=?, 
qq=?, groupId=? where id=? and versionNumber=?
事务2:
session.beginTransaction();
Person p = (Person)session.load(Person.class, 1);
p.setAddress("白驼山");
session.getTransaction().commit();
Hibernate: update t_person set versionNumber=?, name=?, address=?, 
qq=?, groupId=? where id=? and versionNumber=?
org.hibernate.StaleObjectStateException: Row was updated or deleted by 
another transaction (or unsaved-value mapping was incorrect): 
[cn.com.leadfar.hibernate3.Person#1]
23:18:18,843 ERROR AbstractFlushingEventListener:324 - Could not 
synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by
 another transaction (or unsaved-value mapping was incorrect): 
[cn.com.leadfar.hibernate3.Person#1]
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics