Hibernate(24)Hibernate如何实现乐观锁?

Hibernate实现乐观锁

乐观锁(Optimistic Locking)是一种并发控制机制,它允许多个事务并发地读取和修改相同的数据,但在提交时会检查数据是否被其他事务修改过。如果数据被其他事务修改过,当前事务会回滚并重新尝试。乐观锁通常通过版本号(version number)或时间戳(timestamp)来实现。

在Hibernate中,乐观锁可以通过在实体类上添加@Version注解来实现。当一个事务尝试更新数据时,Hibernate会检查版本号或时间戳是否与数据库中的值匹配。如果不匹配,则抛出OptimisticLockException

实现步骤

  1. 创建实体类 并在需要使用乐观锁的字段上添加@Version注解。
  2. 配置Hibernate
  3. 使用Hibernate进行CRUD操作,演示乐观锁的工作原理。

示例代码

配置文件 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.Person"/>
    </session-factory>
</hibernate-configuration>

实体类 Person

在实体类中使用@Version注解。

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

import javax.persistence.*;

@Entity
@Table(name = "person")
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "age")
    private int age;

    @Version
    @Column(name = "version")
    private int version;

    public Person() {}

    public Person(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;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }
}

使用Hibernate进行CRUD操作

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;
    }
}

乐观锁使用示例

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

public class HibernateOptimisticLockExample {
    public static void main(String[] args) {
        // 获取SessionFactory
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();

        // 创建示例数据
        createPerson(sessionFactory);

        // 模拟两个并发事务进行更新操作
        simulateConcurrentUpdates(sessionFactory);

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

    private static void createPerson(SessionFactory sessionFactory) {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        try {
            Person person = new Person("John Doe", 30);
            session.save(person);
            transaction.commit();
            System.out.println("Inserted Person: " + person.getName());
        } catch (Exception e) {
            if (transaction != null) {
                transaction.rollback();
            }
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

    private static void simulateConcurrentUpdates(SessionFactory sessionFactory) {
        // 第一个事务
        Thread thread1 = new Thread(() -> updatePerson(sessionFactory, 1L, "John Doe 1", 31));
        // 第二个事务
        Thread thread2 = new Thread(() -> updatePerson(sessionFactory, 1L, "John Doe 2", 32));

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void updatePerson(SessionFactory sessionFactory, Long personId, String newName, int newAge) {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        try {
            // 获取Person实体
            Person person = session.get(Person.class, personId);
            if (person != null) {
                // 更新Person实体字段
                person.setName(newName);
                person.setAge(newAge);
                // 提交事务前会检查版本号
                session.update(person);
                transaction.commit();
                System.out.println("Updated Person: " + person.getName() + ", Age: " + person.getAge());
            } else {
                System.out.println("No Person found with ID: " + personId);
            }
        } catch (OptimisticLockException e) {
            if (transaction != null) {
                transaction.rollback();
            }
            System.err.println("Optimistic lock exception encountered for Person ID: " + personId);
            e.printStackTrace();
        } catch (Exception e) {
            if (transaction != null) {
                transaction.rollback();
            }
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }
}

详细解释

  1. 配置文件 hibernate.cfg.xml:定义数据库连接信息、Hibernate属性配置以及实体类映射配置。

    xml 复制代码
    <mapping class="com.example.domain.Person"/>
  2. 实体类 Person :使用@Version注解定义版本字段。

    • @Version:表示版本字段,用于乐观锁的实现。每次更新时,Hibernate会检查版本号是否匹配。
    java 复制代码
    @Version
    @Column(name = "version")
    private int version;
  3. HibernateUtil类:创建并管理SessionFactory。

    java 复制代码
    public class HibernateUtil {
        // 创建SessionFactory的代码
    }
  4. CRUD操作类:演示了如何使用Hibernate进行插入操作和模拟并发更新操作。

    java 复制代码
    public class HibernateOptimisticLockExample {
        // CRUD操作方法
    }
  5. 模拟并发更新:使用两个线程来模拟并发更新同一个实体,从而展示乐观锁的工作原理。

    java 复制代码
    private static void simulateConcurrentUpdates(SessionFactory sessionFactory) {
        // 第一个事务
        Thread thread1 = new Thread(() -> updatePerson(sessionFactory, 1L, "John Doe 1", 31));
        // 第二个事务
        Thread thread2 = new Thread(() -> updatePerson(sessionFactory, 1L, "John Doe 2", 32));
    
        thread1.start();
        thread2.start();
    
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

总结

通过在实体类中添加@Version注解,Hibernate可以实现乐观锁。当多个事务并发地修改同一实体时,乐观锁确保了只有版本号匹配的事务才能成功提交,从而避免数据不一致的问题。通过模拟并发更新操作,可以清楚地看到乐观锁的工作原理和效果。

相关推荐
止语Lab1 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活1 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困1 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn1 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
用户298698530141 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel
用户77283104908401 小时前
krono-job:零侵入、单二进制交付的分布式任务调度平台(开源)
后端
Conan在掘金1 小时前
鸿蒙报错速查:Function lacks ending return statement,返回路径漏 return 就炸,根因 + 真解法
后端
人间凡尔赛2 小时前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构
IT_陈寒2 小时前
Vue的响应式让我加班到凌晨3点,原来问题出在这
前端·人工智能·后端
卷无止境2 小时前
提升 Python 代码健壮性的方法大盘点
后端·python