欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Hibernate的并发控制

程序员文章站 2024-03-22 16:44:23
...

①悲观锁

悲观锁是数据库的机制, 并不是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

此时如果有第二个线程进来则会被阻塞在外面:

Hibernate的并发控制

当第一个线程提交后, 第二个线程马上执行, 此时item的库存由100变为96:

Hibernate的并发控制

②乐观锁

悲观锁是有很大缺点的, 因为很多请求都会被阻塞在外面, 所以对性能的影响极大, 一般都不会采用悲观锁的方式

乐观锁就很好的解决了这个问题

乐观锁的机制是在表中追加一个用于版本控制的字段, 该字段会伴随着每次修改而改变, 假如同时有两个线程修改数据库, 则第二个修改的那个线程一定会发现数据库中的版本发生了改变, 此时就会抛异常, 程序就可以捕捉到该异常, 从而使其再次发出请求, 而用户是不会感知到的.

用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字段用于版本控制:

Hibernate的并发控制

在映射文件中:

<?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就无法提交了从而抛异常, 在项目中可以设置程序捕捉到该异常, 从而让其再次发送请求即可.