Spring Boot中的并发控制与事务处理
在开发中,我们经常会遇到多个用户同时对同一行数据进行修改的场景。这时候就需要考虑如何保证数据的一致性和完整性。本文将介绍如何在Spring Boot中实现并发控制,并保证同一个事务对同一行数据的同时修改。
并发控制的概念
并发控制是指在多个事务同时对同一数据进行修改时,如何保证数据的一致性和完整性。常见的并发控制策略包括乐观锁和悲观锁。
- 乐观锁:每个事务在修改数据时都假设其他事务不会同时修改相同的数据。当提交事务时,系统会检查数据是否被其他事务修改过,如果没有则提交成功,否则回滚该事务。
- 悲观锁:每个事务在修改数据时都假设其他事务会同时修改相同的数据。因此,在读取数据时就会对其加锁,在修改数据时再进行解锁。
Spring Boot提供了对乐观锁的支持,可以很方便地实现对同一行数据的同时修改。
实现乐观锁并发控制
步骤一:配置数据库
首先,我们需要在数据库中创建一个示例表,用于演示并发控制。这里我们创建一个名为user
的表,包含id
和name
两个字段。
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`version` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
步骤二:定义实体类
在Spring Boot中,我们使用JPA来操作数据库。首先,需要定义一个实体类User
,对应数据库中的user
表。
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Version
private Long version;
// 省略getter和setter方法
}
在实体类中,我们使用@Version
注解标记了version
字段。这个字段用来记录当前数据的版本号,每次更新数据时,版本号会自动加一。
步骤三:编写业务逻辑
接下来,我们编写业务逻辑,实现对数据库中数据的同时修改。
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void updateUser(Long userId, String newName) {
User user = userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found"));
user.setName(newName);
userRepository.save(user);
}
}
在上面的代码中,我们编写了一个updateUser
方法,用于更新数据库中指定用户的姓名。在方法上添加了@Transactional
注解,表示该方法是一个事务。在方法中,我们首先根据用户ID查询出对应的用户信息,然后修改其姓名,并调用userRepository.save(user)
保存到数据库。
步骤四:测试并发修改
为了模拟并发修改的情况,我们可以使用多线程来同时调用updateUser
方法。
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/updateUser")
public void updateUser() throws InterruptedException {
Thread thread1 = new Thread(() -> {
try {
userService.updateUser(1L, "User1");
} catch (RuntimeException e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
try {
userService.updateUser(1L, "User2");
} catch (RuntimeException e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
在上面的代码中,我们通过创建两个线程thread1
和thread2
,同时调用userService.updateUser
方法来并发修改同一行数据。