springboot配置乐观锁
时间: 2025-07-23 14:09:18 浏览: 1
<think>我们使用Spring Data JPA和@Version注解来实现乐观锁。
乐观锁的基本思想是:在数据库表中增加一个版本号字段(通常命名为version),每次更新数据时,版本号加1。当更新数据时,会检查当前读取到的版本号是否与数据库中的版本号一致,如果一致则更新,否则抛出异常(表示数据已被其他人修改过)。
在Spring Boot中,我们可以通过以下步骤实现:
1. 在实体类中添加一个带有@Version注解的字段,类型可以是Long或者Integer(推荐使用Long)。
2. 使用Spring Data JPA进行数据操作,它会在更新时自动检查版本号。
注意:使用乐观锁时,通常需要结合事务(@Transactional)来使用。
下面是一个具体的例子:
步骤1:创建实体类,包含版本字段
假设我们有一个Product实体,其中包含id、name、price和version字段。
步骤2:创建Repository接口
步骤3:在Service层使用,并在更新操作时处理可能发生的乐观锁异常(ObjectOptimisticLockingFailureException)
代码示例:</think>### Spring Boot配置乐观锁详解
乐观锁通过版本号(Version)或时间戳(Timestamp)实现数据并发控制。Spring Boot中常用`@Version`注解结合JPA实现,以下是详细步骤:
---
#### 1. **添加依赖**
在`pom.xml`中引入JPA和数据库驱动(以H2为例):
```xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
```
---
#### 2. **实体类添加`@Version`注解**
在实体类中定义一个版本字段,用`@Version`标注:
```java
import javax.persistence.*;
import java.util.Date;
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Double price;
// 乐观锁版本字段
@Version
private Long version; // 或使用 @Version private Date updatedAt;
// Getters and Setters
}
```
- **`@Version`字段要求**:
- 类型为`Long`/`Integer`(推荐)或`Date`/`Timestamp`。
- 每次更新自动递增(JPA自动管理)。
---
#### 3. **Repository接口**
创建JPA Repository:
```java
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProductRepository extends JpaRepository<Product, Long> {
}
```
---
#### 4. **Service层处理并发**
在Service中更新数据,捕获乐观锁异常:
```java
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ProductService {
private final ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@Transactional
public void updateProductPrice(Long id, Double newPrice) {
Product product = productRepository.findById(id).orElseThrow();
product.setPrice(newPrice);
try {
productRepository.save(product);
} catch (ObjectOptimisticLockingFailureException e) {
// 处理并发冲突(如重试或通知用户)
throw new RuntimeException("数据已被其他用户修改,请刷新后重试");
}
}
}
```
---
#### 5. **测试乐观锁**
模拟并发场景测试:
```java
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;
@Test
public void testConcurrentUpdate() {
// 模拟两个线程同时更新
Runnable task = () -> productService.updateProductPrice(1L, 99.99);
new Thread(task).start();
new Thread(task).start();
// 第二个线程将抛出ObjectOptimisticLockingFailureException
}
}
```
---
### **关键机制说明**
1. **更新流程**:
- 读取数据时获取当前版本号 `V1`。
- 更新时执行SQL:
```sql
UPDATE product SET price=?, version=V1+1
WHERE id=? AND version=V1; -- 若version已被修改,则更新0行
```
- JPA检测更新行数为0时,抛出`ObjectOptimisticLockingFailureException`。
2. **异常处理建议**:
- **重试机制**:捕获异常后重试操作(需设置最大重试次数)。
- **用户提示**:前端提示“数据已变更,请刷新”。
---
### 注意事项
- **适用场景**:读多写少、冲突概率低的场景(如商品库存)。
- **不适用场景**:高并发写入(如秒杀),此时需结合悲观锁或分布式锁。
- **字段选择**:推荐`Long`类型版本号,避免`Date`的时间精度问题。
---
阅读全文
相关推荐


















