乐观锁是一种并发控制策略,假设多个事务可以同时成功,通过版本号或时间戳来检测冲突。以下是 Hibernate 中乐观锁的完整实现示例。
![图片[1]_Hibernate 乐观锁实现示例_知途无界](https://zhituwujie.com/wp-content/uploads/2026/01/d2b5ca33bd20260111105343.png)
1. 基于版本号的乐观锁(推荐)
实体类配置
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "product_name")
private String productName;
private Double price;
private Integer stock;
// 版本号字段 - 核心注解
@Version
@Column(name = "version")
private Integer version;
@Column(name = "create_time")
private LocalDateTime createTime;
@Column(name = "update_time")
private LocalDateTime updateTime;
// 构造方法
public Product() {}
public Product(String productName, Double price, Integer stock) {
this.productName = productName;
this.price = price;
this.stock = stock;
this.createTime = LocalDateTime.now();
this.updateTime = LocalDateTime.now();
}
// Getter 和 Setter 方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getProductName() { return productName; }
public void setProductName(String productName) { this.productName = productName; }
public Double getPrice() { return price; }
public void setPrice(Double price) { this.price = price; }
public Integer getStock() { return stock; }
public void setStock(Integer stock) { this.stock = stock; }
public Integer getVersion() { return version; }
public void setVersion(Integer version) { this.version = version; }
public LocalDateTime getCreateTime() { return createTime; }
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
public LocalDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime; }
@PreUpdate
public void preUpdate() {
this.updateTime = LocalDateTime.now();
}
@Override
public String toString() {
return "Product{" +
"id=" + id +
", productName='" + productName + '\'' +
", price=" + price +
", stock=" + stock +
", version=" + version +
", createTime=" + createTime +
", updateTime=" + updateTime +
'}';
}
}
对应的数据库表结构
CREATE TABLE products (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
product_name VARCHAR(255) NOT NULL,
price DOUBLE NOT NULL,
stock INT NOT NULL,
version INT DEFAULT 0, -- 版本号字段
create_time DATETIME,
update_time DATETIME
);
2. 基于时间戳的乐观锁
实体类配置
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "order_number")
private String orderNumber;
@Column(name = "customer_name")
private String customerName;
private Double amount;
// 时间戳版本控制 - 核心注解
@Version
@Column(name = "last_updated")
private LocalDateTime lastUpdated;
// 构造方法
public Order() {}
public Order(String orderNumber, String customerName, Double amount) {
this.orderNumber = orderNumber;
this.customerName = customerName;
this.amount = amount;
this.lastUpdated = LocalDateTime.now();
}
// Getter 和 Setter 方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getOrderNumber() { return orderNumber; }
public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; }
public String getCustomerName() { return customerName; }
public void setCustomerName(String customerName) { this.customerName = customerName; }
public Double getAmount() { return amount; }
public void setAmount(Double amount) { this.amount = amount; }
public LocalDateTime getLastUpdated() { return lastUpdated; }
public void setLastUpdated(LocalDateTime lastUpdated) { this.lastUpdated = lastUpdated; }
@Override
public String toString() {
return "Order{" +
"id=" + id +
", orderNumber='" + orderNumber + '\'' +
", customerName='" + customerName + '\'' +
", amount=" + amount +
", lastUpdated=" + lastUpdated +
'}';
}
}
3. Repository/DAO 层实现
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import java.util.Optional;
@Repository
@Transactional
public class ProductRepository {
@PersistenceContext
private EntityManager entityManager;
// 方法1: 使用 JPA 的 save 方法(Spring Data JPA)
public Product save(Product product) {
return entityManager.merge(product);
}
// 方法2: 使用 Hibernate Session 进行更细粒度控制
public Product updateProductStock(Long productId, Integer quantity) {
Session session = entityManager.unwrap(Session.class);
Product product = session.get(Product.class, productId);
if (product != null) {
product.setStock(product.getStock() - quantity);
// Hibernate 会自动处理版本号递增
session.update(product);
}
return product;
}
// 方法3: 悲观锁与乐观锁结合(在特定场景下使用)
public Product updateWithPessimisticLock(Long productId, Integer quantity) {
Session session = entityManager.unwrap(Session.class);
// 先使用悲观锁锁定记录
Product product = session.get(Product.class, productId, LockMode.PESSIMISTIC_WRITE);
if (product != null && product.getStock() >= quantity) {
product.setStock(product.getStock() - quantity);
// 乐观锁仍然有效,版本号会自动递增
session.update(product);
return product;
}
return null; // 库存不足或商品不存在
}
}
4. Service 层业务逻辑(演示乐观锁冲突处理)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.OptimisticLockException;
import javax.persistence.RollbackException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
// 单线程环境下的正常更新
@Transactional
public Product purchaseProduct(Long productId, Integer quantity) {
Product product = productRepository.updateProductStock(productId, quantity);
if (product != null) {
System.out.println("购买成功: " + product);
return product;
} else {
throw new RuntimeException("商品不存在或库存不足");
}
}
// 演示乐观锁冲突处理
@Transactional
public void demonstrateOptimisticLockConflict() {
Long productId = 1L;
try {
// 第一次更新 - 应该成功
Product product1 = productRepository.updateProductStock(productId, 1);
System.out.println("第一次更新成功,版本号: " + product1.getVersion());
// 模拟在内存中修改但不立即提交的情况
product1.setPrice(product1.getPrice() * 1.1);
// 假设此时另一个事务已经提交了更新,版本号已改变
// 这里我们手动模拟版本冲突进行测试
simulateConcurrentUpdate(productId);
// 第二次更新 - 可能遇到乐观锁冲突
productRepository.save(product1);
System.out.println("第二次更新成功");
} catch (OptimisticLockException e) {
System.err.println("检测到乐观锁冲突: " + e.getMessage());
// 处理冲突:重试、回滚或通知用户
handleOptimisticLockException(e);
} catch (RollbackException e) {
System.err.println("事务回滚: " + e.getMessage());
}
}
// 模拟并发更新导致的版本冲突
private void simulateConcurrentUpdate(Long productId) {
// 在实际应用中,这是由另一个线程或事务完成的
// 这里我们直接查询并更新来模拟
Product product = productRepository.updateProductStock(productId, 1);
if (product != null) {
System.out.println("并发更新成功,新版本号: " + product.getVersion());
}
}
// 乐观锁冲突处理策略
private void handleOptimisticLockException(OptimisticLockException e) {
// 策略1: 重试机制
// retryOperation();
// 策略2: 记录日志并通知用户
System.out.println("数据已被其他用户修改,请刷新后重试");
// 策略3: 抛出异常让上层处理
throw new BusinessException("操作失败,数据已被其他用户修改,请重试", e);
}
// 带重试机制的更新操作
@Transactional
public Product updateWithRetry(Long productId, Integer quantity, int maxRetries) {
int retryCount = 0;
while (retryCount < maxRetries) {
try {
return productRepository.updateProductStock(productId, quantity);
} catch (OptimisticLockException e) {
retryCount++;
System.out.println("乐观锁冲突,第 " + retryCount + " 次重试");
if (retryCount >= maxRetries) {
throw new BusinessException("操作失败,经过 " + maxRetries + " 次重试仍然冲突", e);
}
// 等待一段时间后重试
try {
Thread.sleep(100 * retryCount); // 递增等待时间
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
}
return null;
}
// 使用多线程模拟高并发场景
public void concurrentPurchaseSimulation(Long productId, int threadCount) {
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
CompletableFuture<?>[] futures = new CompletableFuture[threadCount];
for (int i = 0; i < threadCount; i++) {
final int threadIndex = i;
futures[i] = CompletableFuture.runAsync(() -> {
try {
Product product = productRepository.updateProductStock(productId, 1);
if (product != null) {
System.out.println("线程 " + threadIndex + " 购买成功,剩余库存: " + product.getStock());
} else {
System.out.println("线程 " + threadIndex + " 购买失败");
}
} catch (OptimisticLockException e) {
System.out.println("线程 " + threadIndex + " 遇到乐观锁冲突: " + e.getMessage());
}
}, executor);
}
// 等待所有任务完成
CompletableFuture.allOf(futures).join();
executor.shutdown();
}
}
5. 自定义异常类
// 自定义业务异常
public class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
}
6. 测试示例
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class OptimisticLockTest {
@Autowired
private ProductService productService;
@Autowired
private ProductRepository productRepository;
@Test
public void testOptimisticLock() {
// 创建测试产品
Product product = new Product("iPhone 15", 999.99, 10);
Product savedProduct = productRepository.save(product);
System.out.println("初始产品: " + savedProduct);
// 测试正常更新
productService.purchaseProduct(savedProduct.getId(), 1);
// 测试乐观锁冲突
productService.demonstrateOptimisticLockConflict();
// 测试并发购买
productService.concurrentPurchaseSimulation(savedProduct.getId(), 5);
}
}
7. 配置说明
application.properties 配置
# Hibernate 配置
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.hibernate.ddl-auto=validate # 生产环境建议使用 validate
# 乐观锁相关配置
spring.jpa.properties.hibernate.connection.provider_disables_autocommit=true
核心要点总结
- @Version 注解:在实体类的版本字段上使用
@Version注解,Hibernate 会自动管理版本号递增和冲突检测。 - 冲突检测:当多个事务同时更新同一数据时,Hibernate 会比较版本号,如果发现版本不匹配,会抛出
OptimisticLockException。 - 冲突处理策略:
- 重试机制:在捕获异常后重新尝试操作
- 用户提示:通知用户数据已被修改,请刷新重试
- 业务回滚:记录日志并回滚事务
- 适用场景:乐观锁适合读多写少、冲突较少的场景。在高冲突场景下,可能需要结合悲观锁使用。
- 版本字段选择:
- @Version Integer/Long:推荐使用,性能好
- @Version Timestamp:精度可能不够,不推荐用于高并发场景
这种实现方式可以有效防止数据不一致问题,同时保持系统的高并发性能。
© 版权声明
文中内容均来源于公开资料,受限于信息的时效性和复杂性,可能存在误差或遗漏。我们已尽力确保内容的准确性,但对于因信息变更或错误导致的任何后果,本站不承担任何责任。如需引用本文内容,请注明出处并尊重原作者的版权。
THE END

























暂无评论内容