在多个客户端可能读取同一笔数据或同时更新一笔数据的情况下,必须要有访问控制的手段,防止同一个数据被修改而造成混乱,最简单的手段就是对数据进行锁定。在自己进行数据读取或更新等动作时,锁定其他客户端不能对同一笔数据进行任何的动作。
悲观锁
悲观锁(Pessimistic Locking),如其名称所示,悲观地认定每次资料存取时,其他的客户端也会存取同一笔数据,因此将会锁住该笔数据,直到自己操作完成后再解除锁。
悲观锁假定任何时刻存取数据时,都可能有另一个客户也正在存取同一笔数据,因而对数据采取了数据库层次的锁定状态,在锁定的时间内其他的客户不能对数据进行存取。对于单机或小系统而言,这并不成问题,然而如果是在网络上的系统,同时间会有许多访问的机器,如果每一次读取数据都造成锁定,其后继的存取就必须等待,这将造成效能上的问题,造成后继使用者的长时间等待。
悲观锁通常透过系统或数据库本身的功能来实现,依赖系统或数据库本身提供的锁机制。Hibernate即是如此,可以利用Query或Criteria的setLockMode()方法来设定要锁定的表或列及其锁模式,可设定的锁模式有以下几个。
LockMode.UPGRADE:利用数据库的for update子句进行锁定。
LockMode.UPGRADE_NOWAIT:使用for update nowait子句进行锁定,在Oracle数据库中使用。
下面来实现一个简单的例子,测试一下采用悲观锁时数据库是如何进行操作的。
Query query = session.createQuery("from User user");
query.setLockMode("user", LockMode.UPGRADE);
该方法在执行查询之前通过Query对象的setLockMode()方法设置了访问User对象的模式, 除了Query对象外,也可以在使用Session的load()或是lock()时指定锁模式
乐观锁
乐观锁(Optimistic Locking)认为资料的存取很少发生同时存取的问题,因而不做数据库层次上的锁定。为了维护正确的数据,乐观锁是使用应用程序上的逻辑来实现版本控制的。
在使用乐观锁策略的情况下,数据不一致的情况一旦发生,有几个解决方法,一种是先更新为主,一种是后更新为主,比较复杂的就是检查发生变动的数据来实现,或是检查所有属性来实现乐观锁。
Hibernate中通过检查版本号来判断数据是否已经被其他人所改动,这也是Hibernate所推荐的方式。在数据库中加入一个version字段记录,在读取数据时连同版本号一同读取,并在更新数据时比较版本号与数据库中的版本号,如果等于数据库中的版本号则予以更新,并递增版本号,如果小于数据库中的版本号就抛出异常。
下面就来在前面例子的基础上进行Hibernate乐观锁的测试。
首先需要修改前面所实现的业务对象,在其中增加一个version属性,用来记录该对象所包含数据的版本信息,修改后的User对象下面代码所示:
修改后的User对象
package com.xuyesheng.hibernate.lock;
public class User {
private String id;
private Integer version; // 增加版本属性
private String name;
private Integer age;
// 省略了getter和setter方法
……
}
然后是修改映射文件,增加version属性的配置。在这里需要注意的是,这里的version属性应该使用专门的<version>元素来进行配置,这样才能使其发挥乐观锁的作用。如果还使用<property>元素来进行配置,那么Hibernate只会将其作为一个普通的属性来进行处理。
修改后的映射文件
<?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 package="cn.hxex.hibernate.lock">
<class name="User" table="USERINFO" optimistic-lock="version">
<id name="id" column="userId"><generator class="uuid.hex"/></id>
<version name="version" column="version" type="java.lang.Integer"/>
<property name="name" column="name" type="java.lang.String"/>
<property name="age" column="age" type="java.lang.Integer"/>
</class>
</hibernate-mapping>
每次对TUser进行更新的时候,我们可以发现,数据库中的version都在递增。而如果我们尝试在tx.commit 之前,启动另外一个Session,对名为Erica 的用户进行操作,以模拟并发更新时的情形:代码内容
Session session = getSession();
Criteria criteria = session.createCriteria(TUser. class );
criteria.add(expression_r_r.eq( " name " , " Erica " ));
Session session2 = getSession();
Criteria criteria2 = session2.createCriteria(TUser. class );
criteria2.add(expression_r_r.eq( " name " , " Erica " ));
List userList = criteria.list();
List userList2 = criteria2.list();
TUser user = (TUser)userList.get( 0 );
TUser user2 = (TUser)userList2.get( 0 );
Transaction tx = session.beginTransaction();
Transaction tx2 = session2.beginTransaction();
user2.setUserType( 99 );
tx2.commit();
user.setUserType( 1 );
tx.commit();
分享到:
相关推荐
Hibernate乐观锁和悲观锁分析,针对在使用HIBERNATE时怎么配置乐观锁进行了详细的范例讲解
其中通过 version 实现的乐观锁机制是 Hibernate 官方推荐的乐观锁实现,同时也是 Hibernate 中,目前唯一在数据对象脱离 Session 发生修改的情况下依然有效的锁机制。因此,一般情况下,我们都选择 version 方式...
Hibernate的乐观锁与悲观锁的文档,帮助学习Hibernate的乐观锁与悲观锁,让你们学得更轻松。
NULL 博文链接:https://12345678.iteye.com/blog/721836
Hibernate锁机制_悲观锁和乐观锁
HibernateTestHibernate乐观锁和悲观锁的学习和实践
Hibernate高级特性,悲观锁与乐观锁的应用和区别,及两者之间的区别。
很好的描述Hibernate的悲观锁和乐观锁,
NULL 博文链接:https://cdxs2.iteye.com/blog/1938245
主要介绍了Hibernate实现悲观锁和乐观锁的有关内容,涉及hibernate的隔离机制,以及实现悲观锁和乐观锁的代码实现,需要的朋友可以了解下。
求助编辑百科名片相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库 性能的大量开销,特别是对长事务而言,...
NULL 博文链接:https://jordanyangwei.iteye.com/blog/313756
乐观锁version 对于在Hibernate中的乐观锁和悲观锁的学习
资源名称:Hibernate基础教程汇总资源目录:【】hibernate【】hibernate_312_中文参考文档【】hibernate乐观锁和悲观锁【】hibernate基础教程【】Hibernate注解详解实体类大全资源截图: 资源太大,传百度网盘了,...
主要介绍了Hibernate悲观锁和乐观锁实例详解,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
hibernate利用锁的相关功能详解,分为乐观锁和悲观锁。希望各位喜欢。
性能------------缓存 延迟加载 事务 悲观 乐观锁
该资源主要是分析:乐观锁解决事务并发.乐观锁校验测试, 基于hiber管理的悲观锁实现