Hibernate的并发控制
①悲观锁
悲观锁是数据库的机制, 并不是Hibernate框架提供的, 例如: select * from item for update, 此时数据库就被上锁了, A在查询时, B是不能查询的, 是被阻塞在外面的
新建一个Item类:
package com.rl.hiber.model;
public class Item {
private Integer itemId;
private String itemName;
private Integer stock;
public Integer getItemId() {
return itemId;
}
public void setItemId(Integer itemId) {
this.itemId = itemId;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
}
映射文件就不贴上来了
测试代码:
@Test
public void updateStock(){
Session session = HibernateUtil.getSessoion();
Transaction tx = session.beginTransaction();
try {
//设置悲观锁, 当有一个线程在查询的时候, 此时其他的线程都被阻塞在外面, 只有当其提交之后, 悲观锁才会释放
Item item = (Item) session.load(Item.class, 1, LockOptions.UPGRADE);
item.setStock(item.getStock()-2);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}finally{
HibernateUtil.closeResource(session);
}
}
打个断点, 很明显可以看出查询语句是带悲观锁的(for update)
Hibernate: select item0_.item_id as item_id1_0_0_, item0_.item_name as item_nam2_0_0_, item0_.stock as stock3_0_0_ from t_item item0_ where item0_.item_id=? for update
此时如果有第二个线程进来则会被阻塞在外面:
当第一个线程提交后, 第二个线程马上执行, 此时item的库存由100变为96:
②乐观锁
悲观锁是有很大缺点的, 因为很多请求都会被阻塞在外面, 所以对性能的影响极大, 一般都不会采用悲观锁的方式
乐观锁就很好的解决了这个问题
乐观锁的机制是在表中追加一个用于版本控制的字段, 该字段会伴随着每次修改而改变, 假如同时有两个线程修改数据库, 则第二个修改的那个线程一定会发现数据库中的版本发生了改变, 此时就会抛异常, 程序就可以捕捉到该异常, 从而使其再次发出请求, 而用户是不会感知到的.
用sql实现:
update t_item t set t.stock = 100 - 2, t.version = t.version + 1 where t.item_id = 1001 and t.version = 1
其实Hibernate框架会帮我们实现这个过程, 我们只需要在Item类中追加一个version字段用于版本控制:
在映射文件中:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rl.hiber.model">
<class name="Item" table="t_item">
<id name="itemId" column="item_id">
<generator class="native"></generator>
</id>
<!-- 乐观锁的版本字段映射
-->
<version name="version"></version>
<property name="itemName" column="item_name"></property>
<property name="stock"></property>
</class>
</hibernate-mapping>
注意: 此时version标签一定要在property标签之前
现在执行悲观锁的那个流程的话, 可以发现, 当线程1还没提交之前, 线程2可以直接减库存, 但是线程1就无法提交了从而抛异常, 在项目中可以设置程序捕捉到该异常, 从而让其再次发送请求即可.
推荐阅读
-
Hibernate的并发控制
-
【并发控制】并发控制与分布式锁(redis/zookeeper)实现【图文教程】_ 第3章
-
MySql学习之事务和并发控制
-
【Android】RxJava的使用(四)线程控制 —— Scheduler
-
HSQL 在 Hibernate 中的配置 博客分类: Hibernate hsqlhibernate
-
并发控制锁表 博客分类: ORACLE 数据库 并发锁表控制
-
hibernate中映射长文本类型的通用方式 博客分类: java HibernateMySQLOraclePostgreSQL
-
动手开发自己的mvc-1----实现初步的控制层,实现各种配置和资源获取 博客分类: 实现自己的MVCjava综合 多线程java框架mvc
-
动手开发自己的mvc-2----完善控制层,提供自动注入和注解上传等功能 博客分类: 实现自己的MVCjava综合 java框架xmlmvc
-
Spring与Hibernate的整合 博客分类: 整合 hibernatespring