所谓的加锁是为了解决程序的高并发访问问题,对一些资源的访问进行控制,加锁的两种方式就分为悲观锁和乐观锁。本篇博客我们一起探索这两种锁之间的关系。
悲观锁:
悲观锁是由数据库机制实现的,在整个过程中把数据锁住,只要事务不释放(提交或者回滚),任何人都不能查看或者修改数据,只能处于等待状态。
悲观锁的应用非常简单,只需要在sql语句后面加上一个“for update”即可,此sql语句告诉数据库锁定相关的数据。如:Inventoryinv=(Inventory)session.load(Inventory.class,"1001",LockMode.UPGRADE);在使用load方法时,将最后一个参数的LockMode设置为UPGRADE。
虽然悲观锁的使用在Hibernate中很方便,但是由于数据一旦被锁定,就只能等待释放,如果有用户一直占用资源不释放,那么其他人就永远无法使用该资源,这会造成资源的极大浪费,所以实际应用中很少使用悲观锁,一般使用乐观锁。
乐观锁:
乐观锁实际是一种冲突检测的方式,采用对数据加上一个版本控制的方式,一般在数据库中加入一个version字段在读取数据的时候讲version读取出来,在保存数据的时候判断version的值是否小于数据库中的version值,如果小于不予更新,否则予以更新。
Hibernate中对于乐观锁的实现提供了三种方式,一种是增加一个version字段作为版本标识,映射文件中提供了一个optimistic-lock属性。映射文件如下:
<hibernate-mapping>
<class name="com.bjpowernode.hibernate.Inventory" table="t_inventory" optimistic-lock="version">
<id name="itemNo">
<generator class="assigned"/><!-- 设置主键生成策略为手动分配 -->
</id>
<version name="version"/>
<property name="itemName"/>
<property name="quantity"/>
</class>
</hibernate-mapping>
第二种方式和前面一种思想是一样的,只是把version字段换成了timestamp,采用时间戳的方式完成。
第三种方式基于遗留项目方式,如果数据库中无法添加上面提到的那种字段为本版本控制标识,我们将无法使用上面的两种方式,只能使用这种基于遗留项目的这种方式。在配置文件中设置optimistic-lock="all"。设置dynamic-update="true"。
映射文件如下:
<hibernate-mapping>
<class name="com.bjpowernode.hibernate.Inventory" table="t_inventory" optimistic-lock="all" dynamic-update="true">
<id name="itemNo">
<generator class="assigned"/>
</id>
<!-- <version name="version"/> -->
<!--<timestamp name="updateDate"/> -->
<property name="itemName"/>
<property name="quantity"/>
</class>
</hibernate-mapping>
Hibernate提供的这两种锁的机制限制了数据库的并发性的操作,保证了数据的完整性,悲观锁对数据库的依赖性很强,在大数据量的操作上会增加数据库的负担,相比之下,实际应用会选择乐观锁。