业务实现过程中,难免需要保证数据访问的排他性。如金融系统的日终结算中,我们会针对某个截止点的数据进行处理,在此同时,不希望在结算的这段时间里(几秒钟或者几个钟头),数据再有变动,不然我们的统计也无效了。这种时候,我们为了保证某些数据在某个操作中不被外界修改,就引入了我们hibernate的锁机制,一旦我们给目标上锁,其他程序则不能去修改。hibernate中锁是两种,悲观和乐观锁。、
悲观锁,自然,它指的是对数据被外界(包括本系统当前的其他事物,以及来自外部系统的事务处理)修改,保持保守态度,因此整个处理过程中,数据一致处于被锁定的状态【额....还真是够悲观的..】,悲观锁的实现,往往依靠数据提供的锁机制(也只有数据库层提供的锁机制,才能真正保证排他性,否则本系统中实现加锁机制,并不能一定保证外部系统不会修改数据)。
下面,这是一个典型的,依赖数据库实现的悲观锁的调用:
- select * from account where name="Erica" for update
select * from account where name="Erica" for update
通过for update子句,sql锁定了account中,所以符合名字等于erica的记录。即在本次事物提交之前(事物提交时会释放事物过程中的锁),外界无法修改这些记录。hibernate的悲观锁,也是基于数据库的锁机制实现的,下面代码实现了对查询记录的加锁:
- String hqlStr="from User user where uesr.name='Erica' ";
- Query query=session.createQuery(hqlStr);
-
query.setLockMode("user",LockMode.UPGRADE); //加锁
- List userList=query.list(); //获得数据
String hqlStr="from User user where uesr.name='Erica' ";
Query query=session.createQuery(hqlStr);
query.setLockMode("user",LockMode.UPGRADE); //加锁
List userList=query.list(); //获得数据
当运行后,我们通过show_sql可以看见,原生的sql语句仍然在最后面加上了for update,可以看出hibernate通过数据库的for update自己来实现了悲观锁的机制。
Hibernate的加锁模式则分为:LockMode.NONE:无锁 LockMode.WRITE:hibernate在insert和update记录的时候会自动获取 LockMode.READ: hibernate在读取数据的时候会自动获取。
以上的3中机制,一般由hibernate内部使用,如hibernate为了保证update过程中对象不被外界修改,会在save方法实现中自动为目标对象加上WRITE锁,这些都是hibernate内部数据的锁定机制,与数据库无关。
而LockMode.UPGRADE:利用数据的for update子句加锁, LockMode.UPGRADE_NOWAIT: oracle特定的实现,利用oracle的for update nowait子句实现加锁。
上面这两种所机制是我们比较常用的,依赖数据库的悲观锁机制。加锁方式一般有:Criteria.setLockMode ; Query.setLockMode ; Session.lock,注意,只有在查询开始前(也就是hibernate生成sql前)设定加锁,才会真正通过数据库的锁机制来加锁处理,否则,数据已经通过不包含for update的子句的select 语句加载了,锁机制也无从谈起。
乐观锁,则采取更加宽松的机制加锁。悲观的大都靠数据库的for update实现,在保证了独占性外,性能会相对消耗较大。对于长事物而言,基本无法承受额样的开销。而乐观锁则是通过给数据库增加一个version字段,通过比较版本信息,从而实现加锁机制。下面我们打个比方,就知道了。
数据库的accout表中,记录着张三的账户上有100块钱,此时银行还未上班,张三也未存取或转账,这时,乐观锁情况下,这个100块是version=1的,而然在上班之后,银行操作员a,将账户查了出来,在进行扣除,他想着要扣多少钱才好,于是他边喝咖啡边想,这时候,操作员b也来了,查到了张三的账户也想扣他的钱,这时a想好了,先行扣了张三的50块,即100-50,然后愉快的提交了,此时张三账户的version字段被+1,成为version=2了。后来b了想好了,扣了它20块,即100-20,他也想提交,但是突然报出提示,不能提交,缘故是提交的版本必须大于记录的版本才能执行,哦,他这才知道原来张三的账户已经被修改过了,所以才被驳回。
从上面的例子可以看出,乐观锁避免了长事务中的数据加锁的开销,操作员a和b在操作时,都未加锁,从而大大提升了大并发量的系统的整体的性能表现。而hibernate在其数据访问引擎中内置了对乐观锁的实现。如果不考虑外部系统对数据库的更新操作,利用hibernate提供的透明化的乐观所机制将大大提升生产力。
比如,我们为之前几篇博文中的User表加上乐观锁,即添加optimistic-lock属性:
- <hibernate-mapping>
-
<class name="com.entity.Uesr" table="User" dynamic-update="true " dynamic-insert="true" optimistic-lock="vsersion">
-
<SPAN style="WHITE-SPACE: pre"> </SPAN><version column="version" name="version" type="java.lang.Integer"/>
- ......
-
</class>
-
</hibernate-mapping>
<hibernate-mapping>
<class name="com.entity.Uesr" table="User" dynamic-update="true " dynamic-insert="true" optimistic-lock="vsersion">
<version column="version" name="version" type="java.lang.Integer"/>
......
</class>
</hibernate-mapping>
上面的optimistic-lock属性则可选为none(无乐观锁);version(通过版本机制实现乐观锁);dirty(通过检查发生变动的属性实现乐观锁); all(通过检查所有属性实现乐观锁),其中version方式是hibernate官方推荐的方式,所以我在上面的举例,也是通过version的方式。
下面,我们尝试去更新User的记录,代码如下:
- Criteria ct=session.createCriteria(User.class);
-
ct.add(Expression.eq("name","Erica"));
-
- List userList=ct.list();
-
User user=(User)userList.get(0);
-
- Transaction tx=session.beginTransaction();
-
user.setUserType(1)';
- tx.commit();
Criteria ct=session.createCriteria(User.class);
ct.add(Expression.eq("name","Erica"));
List userList=ct.list();
User user=(User)userList.get(0);
Transaction tx=session.beginTransaction();
user.setUserType(1)'; //更新UserType字段为1
tx.commit();
每次去更新的时候,我们可以发现,数据库的version一直在加1。如果我们在tx.commit之前,再启动一个session去对Erica进行更新,以模拟并发更新的话,那么代码就是这样的:
- Session session=getSession();
-
Criteria ct=session.createCriteria(User.class);
-
ct.add(Expression.eq("name","Erica"));
-
- Session session2=getSession();
-
Criteria ct2=session.createCriteria(User.class);
-
ct.add(Expression.eq("name","Erica"));
-
- List list1=ct.list();
- List list2=ct2.list();
-
User user1=(User)list1.get(0);
-
User user2=(User)list2.get(0);
-
- Transaction tx1=session.beginTransaction();
- Transaction tx2=session2.beginTransaction();
-
-
user2.setUserType(99);
- tx2.commit();
-
user2.setUserType(1);
- tx1.commit();
Session session=getSession();
Criteria ct=session.createCriteria(User.class);
ct.add(Expression.eq("name","Erica"));
Session session2=getSession();
Criteria ct2=session.createCriteria(User.class);
ct.add(Expression.eq("name","Erica"));
List list1=ct.list();
List list2=ct2.list();
User user1=(User)list1.get(0);
User user2=(User)list2.get(0);
Transaction tx1=session.beginTransaction();
Transaction tx2=session2.beginTransaction();
user2.setUserType(99);
tx2.commit();
user2.setUserType(1);
tx1.commit();
那么,这段代码执行时会在tx1.commit()处,抛出StaleObjectStateException异常,指出版本检查失败,通过这个异常,我们就可以进行相应的处理。
到这里,假使看的认真的话,我们大概可以知道下面这几点,第一,hibernate锁是用来做什么的一般用于哪些场合或者系统;第二,什么是悲观锁,乐观锁,它们的机制是怎么样的。第三,基础的一些悲观锁乐观锁的使用方法和实际能解决的问题。希望我说的这三点,大家都能有所苟同。
转自:http://jackjobs.iteye.com/blog/1720389
分享到:
相关推荐
Hibernate 锁机制_悲观锁和乐观锁 Hibernate 锁机制是指在数据库访问中,为了维护数据的一致性和正确性,所采取的一些机制来防止数据的并发访问和修改。 Hibernate 中有两种锁机制:悲观锁和乐观锁。 一、悲观锁...
Hibernate,作为Java领域广泛使用的ORM框架,提供了一种处理并发数据访问冲突的手段,那就是锁机制。主要分为两种类型:乐观锁(Optimistic Locking)和悲观锁(Pessimistic Locking)。 **悲观锁(Pessimistic ...
悲观锁的实现主要依赖于数据库级别的锁机制,这是唯一能够真正保证数据访问排他性的方法。即使应用程序内部实现了加锁机制,也无法完全避免外部系统对数据的修改。 **典型应用案例**: 考虑一个场景,需要查询账户...
Hibernate 简介 Hibernate 开发流程 Hibernate 配置文件 Hibernate 核心接口和类 Hibernate ORM映射 HQL Hibernate 懒加载机制与抓取策略 Hibernate 缓存 Hibernate 锁机制
在Java的持久化框架Hibernate中,锁定机制是数据库并发控制的重要组成部分,用于处理多线程或分布式环境下的数据一致性问题。悲观锁和乐观锁是两种常见的锁定策略,它们各有特点,适用于不同的场景。 **悲观锁**...
【hibernate事务管理机制】是指在使用Hibernate框架进行数据库操作时,如何管理和协调事务的一系列规则和策略。事务管理是确保数据一致性、完整性和并发控制的重要机制。 **悲观锁**是预防性的锁定策略,它假设并发...
本文主要讨论的是Hibernate框架中两种锁机制的使用:乐观锁和悲观锁。 首先,让我们深入理解悲观锁(Pessimistic Locking)。悲观锁正如其名字所示,假设并发环境中数据会被频繁修改,所以在整个数据处理过程中,它...
悲观锁是一种较为传统的锁机制,它假定并发冲突的可能性很高,因此在整个数据处理过程中都会锁定数据。这种机制能够有效防止数据被其他事务修改,但同时也可能导致较高的性能开销。 ##### 2.2 实现方式 悲观锁主要...
4. ** Hibernate锁的使用** - `Session.lock()`: 将对象锁定在指定的锁模式下。 - `Query.setLockMode()`: 对查询结果应用锁模式。 - `Criteria.setLockMode()`: 同上,针对Criteria查询。 5. **实验情况** - ...
在乐观锁的机制下,假设并发用户很少会发生冲突,所以在读取数据时不会进行任何锁定,而在更新数据时才会检查在此期间是否有其他用户修改过该数据。如果检测到有其他用户修改,更新操作将会失败,通常会抛出一个异常...
数据库事务是数据库操作的核心组成部分,它确保了数据的一致性和完整性。事务是由一系列数据库操作组成的逻辑单元,这些操作要么全部成功...了解并熟练掌握这两种锁机制,有助于设计出更高效、更稳定的并发控制策略。
本文将深入探讨这两种锁机制的原理、应用场景及其区别。 首先,我们来理解悲观锁的概念。悲观锁正如其名,持有一种悲观的态度,认为数据在任何时候都可能被其他线程修改,因此在读取数据时会立即加上锁,确保在读取...
《Hibernate 悲观锁与乐观锁详解》 在多用户并发访问的环境中,数据库管理系统必须具备有效的数据访问控制机制,以确保数据的一致性和完整性...开发者应根据实际应用场景选择合适的锁机制,以平衡数据安全和系统性能。
Hibernate锁的基本机制!主要是针对Hiberante乐观锁悲观锁的介绍以及相对应的数据库本身的处理机制,包括和SPRING相结合部分的锁处理机制
本文将深入探讨 Hibernate 版本的乐观锁机制,特别是通过XML配置方式实现这一机制。乐观锁是一种非阻塞锁,它假设在并发环境下数据冲突的概率较低,因此在读取数据时不加锁,而在更新数据时检查在此期间是否有其他...
求助编辑百科名片相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库 性能的大量开销,特别是对长事务而言,...
在本文中,我们将详细介绍 Hibernate 实现悲观锁和乐观锁的代码实现,并讨论 Hibernate 的隔离机制和锁机制。 Hibernate 的隔离机制 Hibernate 的隔离机制是基于数据库的事务隔离级别的。 Hibernate 提供了四种...