Hibernate(17)什么是Hibernate的悲观锁?

Hibernate的悲观锁(Pessimistic Locking)是一种并发控制机制,用于防止多个事务同时访问和修改相同的数据记录。与乐观锁不同,悲观锁在读取数据时即对数据进行锁定,以确保后续的事务在访问和修改数据时必须等待该锁释放,从而避免数据冲突。

悲观锁的实现

悲观锁通常通过数据库的锁机制实现,例如行级锁、表级锁等。Hibernate提供了几种方式来实现悲观锁,主要使用LockMode枚举类和Session中的锁定方法。

示例代码

下面是一个完整的示例,展示如何在Hibernate中使用悲观锁。

配置文件 hibernate.cfg.xml

xml 复制代码
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- 数据库连接配置 -->
        <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/your_database</property>
        <property name="hibernate.connection.username">your_username</property>
        <property name="hibernate.connection.password">your_password</property>

        <!-- Hibernate 属性配置 -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">update</property>

        <!-- 映射类配置 -->
        <mapping class="com.example.domain.Student"/>
    </session-factory>
</hibernate-configuration>

HibernateUtil 类

java 复制代码
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    private static final SessionFactory sessionFactory;

    static {
        try {
            // 从配置文件创建SessionFactory
            sessionFactory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
        } catch (Throwable ex) {
            // 记录启动失败的错误
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

实体类 Student

java 复制代码
package com.example.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    private int age;

    public Student() {}

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // getters 和 setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

悲观锁示例代码

下面的示例展示了如何在并发环境中使用悲观锁。

java 复制代码
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

public class HibernatePessimisticLockingExample {
    public static void main(String[] args) {
        // 获取SessionFactory
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        
        // 第一个会话
        Session session1 = sessionFactory.openSession();
        Transaction transaction1 = session1.beginTransaction();
        
        // 第二个会话
        Session session2 = sessionFactory.openSession();
        Transaction transaction2 = session2.beginTransaction();

        try {
            // 在第一个会话中获取对象并加锁
            Student student1 = session1.get(Student.class, 1L, LockMode.PESSIMISTIC_WRITE);
            System.out.println("Session1 - Student: " + student1.getName() + ", Age: " + student1.getAge());

            // 在第二个会话中尝试获取相同的对象并加锁
            Student student2 = session2.get(Student.class, 1L, LockMode.PESSIMISTIC_WRITE);
            System.out.println("Session2 - Student: " + student2.getName() + ", Age: " + student2.getAge());

            // 修改并更新对象
            student1.setAge(21);
            session1.update(student1);
            transaction1.commit();
            System.out.println("Session1 - Updated Student: " + student1.getName() + ", Age: " + student1.getAge());

            // 尝试在第二个会话中提交修改
            student2.setAge(22);
            session2.update(student2);
            transaction2.commit(); // 这里会等待直到第一个事务提交或回滚

        } catch (Exception e) {
            if (transaction2 != null) {
                transaction2.rollback();
            }
            e.printStackTrace();
        } finally {
            if (session1 != null) {
                session1.close();
            }
            if (session2 != null) {
                session2.close();
            }
        }

        // 关闭SessionFactory
        sessionFactory.close();
    }
}

详细解释

  1. 配置悲观锁 :在获取实体对象时使用LockMode来指定锁定模式。

    java 复制代码
    Student student1 = session1.get(Student.class, 1L, LockMode.PESSIMISTIC_WRITE);
  2. 示例场景

    • 在第一个会话中获取同一个Student对象,并使用悲观锁进行锁定。
    • 在第二个会话中尝试获取同一个Student对象,并同样使用悲观锁进行锁定。
    • 第二个会话在获取对象时会被阻塞,直到第一个会话提交或回滚事务。
    • 第一个会话修改并更新对象,提交事务。
    • 第二个会话在第一个会话提交后获取锁,进行修改并提交事务。
  3. 事务管理:在事务中进行数据操作,并在操作完成后提交事务。如果操作失败,则回滚事务以确保数据一致性。

总结

悲观锁是一种有效的并发控制机制,通过在读取数据时即进行锁定,确保在事务提交之前其他事务无法访问和修改该数据。在Hibernate中,通过LockMode.PESSIMISTIC_WRITE等锁定模式可以方便地实现悲观锁。理解并正确应用悲观锁,可以有效地避免数据冲突,确保数据一致性和正确性。

相关推荐
大黄评测1 分钟前
jQuery 遍历方法 each 实战:循环处理列表数据
后端
大勇前进9 分钟前
jQuery 事件委托原理:彻底解决动态生成元素绑定失效问题
后端
苍何19 分钟前
做了个微信聊天爆款视频 Skill, Codex / WorkBuddy一键复刻使用
后端·aigc
卷无止境25 分钟前
用 FastAPI 撑起大文件的上传下载:从流式处理到断点续传的完整实践
后端·python·fastapi
铁皮饭盒25 分钟前
网页端, 6.5MB人脸识别模型, 谷歌框架, 又快又准
前端·javascript·后端
长大198831 分钟前
jQuery AJAX 完整封装教程:告别重复写请求代码
后端
worxfr2 小时前
Go 并发控制:从 Channel 方向约束到实战模式
开发语言·后端·golang
拾陆楼2 小时前
PT: DMSA辅助调tree报告前后级余量脚本
后端·学习
今天的砖头有点烫手啊2 小时前
接口太慢?Spring Boot 缓存体系 @Cacheable 全链路拆解
spring boot·后端·缓存
雪隐3 小时前
个人电脑玩AI-16让5060 Ti给你打工——MiniMax H3 提速实录:从"泡杯茶等视频"到"视频等你",一张 5060 Ti 的自我修养
前端·人工智能·后端