悲观锁与乐观锁
悲观锁
悲观锁(pessimistic lock)是指在每次操作数据时,总是悲观地认为会有其他事务操作同一数据,因此,在整个数据处理过程中,会把数据处于锁定状态。
悲观锁具有排他性,一般由数据库实现。在锁定时间内,其他事务不能对数据进行存取等操作,这可能导致长时间的等待问题。
通常在应用中会设定如下两种锁模式。
1)LockMode.UPGRADE
该模式不管缓存中是否存在对象,总是通过 select 语句到数据库中加载该对象,如果映射文件中设置了版本元素,则执行版本检查,比较缓存中的对象是否与数据库中对象的版本一致,如果数据库系统支持悲观锁(如 MySQL),则执行 elect…for update 语句,如果不支持(如 Sybase),则执行普通 select 语句。
2)LockMode.UPGRADE_NOWAIT
该模式与 LockMode.UPGRADE 具有同样的功能,是 Oracle 数据库特有的锁模式,会执行 select…for update nowait 语句。
nowait 表示如果执行 select 语句的事务不成立则获得悲观锁,它不会等待其他事务释放锁,而是立刻抛出锁定异常。
下面通过丢失更新的案例演示悲观锁的使用。
案例主要代码:
@Getter @Setter public class Inventory { private int itemNo; private String itemName; private int quantity; }
<hibernate-mapping> <class name="com.bjsxt.hibernate.Inventory" table="t_inventory"> <id name="itemNo"> <generator class="native"/> </id> <property name="itemName"/> <property name="quantity"/> </class> </hibernate-mapping>
使用:
Inventory inv = (Inventory)session.load(Inventory.class, 1, LockMode.UPGRADE);
悲观锁不支持懒加载,也就是说一加锁懒加载就失效了
乐观锁
相对于悲观锁而言,乐观锁(optimistic lock)通常认为多个事务同时操作同一数据的情况很少发生,因此乐观锁不进行数据库层次上的锁定,而是基于数据版本(Version)标识实现应用程序级别上的锁定机制,这既能保证多个事务的并发操作,又能有效防止第二类丢失更新的发生。
数据版本标识是通过为数据表增加一个 version 字段实现的。增加 version 字段后,程序在读取数据时,会将版本号一同读出,之后在更新此数据时,会将此版本号加一。
在提交数据时,将现有的版本号与数据表对应记录的版本号进行对比,如果提交数据的版本号大于数据表中的版本号,则允许更新数据,否则禁止更新数据。
主要代码:
@Setter @Getter public class Inventory { private int itemNo; private String itemName; private int quantity; private int version1; }
<hibernate-mapping> <class name="com.bjsxt.hibernate.Inventory" table="t_inventory" optimistic-lock="version"> <id name="itemNo"> <generator class="native"/> </id> <version name="version1"/> <property name="itemName"/> <property name="quantity"/> </class> </hibernate-mapping>
Hibernate 的乐观锁是以 id 和 version 决定更新对象的。