Hibernate(38)如何在Hibernate中配置乐观锁?

Hibernate中的乐观锁配置

在Hibernate中,乐观锁(Optimistic Locking)用于确保并发事务的安全性,防止数据被多个事务同时修改而导致的不一致。乐观锁的基本原理是在修改数据时检查数据的版本信息,确保数据没有被其他事务修改。

配置乐观锁的示例代码

实体类定义

假设我们有一个实体类 Product,我们将使用乐观锁来确保多个事务不会同时修改同一个产品的数据。

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

import javax.persistence.*;

@Entity
@Table(name = "product")
public class Product {

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

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

    @Column(name = "price")
    private Double price;

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

    public Product() {}

    public Product(String name, Double price) {
        this.name = name;
        this.price = price;
    }

    // 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 Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }
}
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.Product"/>
    </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;
    }
}

使用乐观锁的示例

插入示例数据
java 复制代码
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

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

        // 插入示例数据
        insertSampleData(sessionFactory);

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

    private static void insertSampleData(SessionFactory sessionFactory) {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        try {
            Product product1 = new Product("Laptop", 1000.0);
            Product product2 = new Product("Smartphone", 500.0);

            session.save(product1);
            session.save(product2);

            transaction.commit();
            System.out.println("Inserted sample data");
        } catch (Exception e) {
            if (transaction != null) {
                transaction.rollback();
            }
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }
}
更新操作示例
java 复制代码
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

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

        // 模拟两个事务同时更新一个产品
        simulateConcurrentUpdates(sessionFactory);

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

    private static void simulateConcurrentUpdates(SessionFactory sessionFactory) {
        // 事务1
        Thread transaction1 = new Thread(() -> {
            Session session = sessionFactory.openSession();
            Transaction transaction = session.beginTransaction();
            try {
                Product product = session.get(Product.class, 1L); // 获取ID为1的产品
                product.setPrice(product.getPrice() + 100); // 增加价格
                session.update(product);
                transaction.commit();
                System.out.println("Transaction 1 committed");
            } catch (Exception e) {
                if (transaction != null) {
                    transaction.rollback();
                }
                e.printStackTrace();
            } finally {
                if (session != null) {
                    session.close();
                }
            }
        });

        // 事务2
        Thread transaction2 = new Thread(() -> {
            try {
                // 让事务2稍微延迟,确保事务1先开始
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Session session = sessionFactory.openSession();
            Transaction transaction = session.beginTransaction();
            try {
                Product product = session.get(Product.class, 1L); // 获取ID为1的产品
                product.setPrice(product.getPrice() + 200); // 增加价格
                session.update(product);
                transaction.commit();
                System.out.println("Transaction 2 committed");
            } catch (Exception e) {
                if (transaction != null) {
                    transaction.rollback();
                }
                e.printStackTrace();
            } finally {
                if (session != null) {
                    session.close();
                }
            }
        });

        // 启动两个事务线程
        transaction1.start();
        transaction2.start();

        // 等待两个事务执行完毕
        try {
            transaction1.join();
            transaction2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

详细解释

  1. 实体类定义

    • Product 类定义了产品的信息,包括 id, name, priceversion 字段。
    • @Version 注解应用于 version 字段。这是Hibernate用来实现乐观锁的关键注解。每当实体被更新时,version 字段会自动递增。
  2. Hibernate配置文件

    • 标准的Hibernate配置文件,用于数据库连接和映射类的配置。
  3. HibernateUtil类

    • 一个实用类,用来创建和返回 SessionFactory 实例。
  4. 插入示例数据

    • 创建 Product 对象,并将其保存到数据库。
  5. 更新操作示例

    • 模拟两个并发事务同时更新同一个产品。
    • 两个事务分别读取产品数据并进行更新。
    • 在事务提交时,Hibernate会检查 version 字段是否一致。如果有另一个事务已经修改了该产品,version 字段会发生变化,导致当前事务抛出 OptimisticLockException 并回滚。

通过这种方式,Hibernate确保了在并发环境下的数据一致性,避免了多个事务同时修改同一条记录而导致的数据冲突。

相关推荐
野犬寒鸦几秒前
从零起步学习JVM || 第一章:类加载器与双亲委派机制模型详解
java·jvm·数据库·后端·学习
ujainu7 分钟前
告别杂乱!Flutter + OpenHarmony 鸿蒙记事本的标签与分类管理(三)
android·flutter·openharmony
黎雁·泠崖16 分钟前
【魔法森林冒险】2/14 抽象层设计:Figure/Person类(所有角色的基石)
java·开发语言
怒放吧德德1 小时前
后端 Mock 实战:Spring Boot 3 实现入站 & 出站接口模拟
java·后端·设计
常利兵1 小时前
Android内存泄漏:成因剖析与高效排查实战指南
android
·云扬·1 小时前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
野生技术架构师1 小时前
SQL语句性能优化分析及解决方案
android·sql·性能优化
biyezuopinvip1 小时前
基于Spring Boot的企业网盘的设计与实现(任务书)
java·spring boot·后端·vue·ssm·任务书·企业网盘的设计与实现
脸大是真的好~1 小时前
EasyExcel的使用
java·excel
小宋10211 小时前
Java 项目结构 vs Python 项目结构:如何快速搭一个可跑项目
java·开发语言·python