因此,我有一个非常简单的基于Spring Boot的Web应用程序。
该数据库有一个表user
,具有列id
和username
以及一个记录:(1, 'Joe')
。
我也有以下课程:
User
-映射到表user
UserRepository
-用于它的Spring Data JPA存储库
UserService
+ DefaultUserService
-具有CRUD方法的服务层
UserController
-具有两种方法的控制器:get
和update
Application
-主类(带有@EnableTransactionManagement
注释
就可以了
因此,我要尝试的是测试事务隔离级别READ_COMMITTED
。我正在同时发送两个请求:
/update
,这将更新用户,将其名称设置为Jack
,然后将当前线程休眠5秒钟,然后提交事务。/get
,在每次尝试后,该用户反复读取同一用户10次,小睡1s。问题在于,即使已提交(1)的事务,(2)仍会返回旧值-Joe
。如果此后我尝试向/get
发送另一个请求,它会按预期返回Jack
,因此只有在事务(1)提交对事务(1)的更改之前开始事务(2)的情况下,该问题才会发生数据库。
您可能会在下面找到一些代码以供参考。
服务:
@Service
public class DefaultUserService implements UserService {
... //fields & constructor
@Override
@SneakyThrows
@Transactional(isolation = Isolation.READ_COMMITTED)
public User read(Long id) {
User user = userRepository.findById(id).get();
Thread.sleep(1000);
return user;
}
@Override
@SneakyThrows
@Transactional
public User update(Long id, User update) {
log.info("Entering update method for user {}", id);
User user = read(id);
user.setUsername("Jack");
user = userRepository.save(user);
log.info("User {} updated, falling asleep for 5s", id);
Thread.sleep(5000);
return user;
}
}
控制器:
@RestController
public class UserController {
... //fields & constructor
@RequestMapping("/update")
public User update() {
User user = userService.update(1L, new User("Jack"));
log.info("UPDATED: {}", user);
return user;
}
@RequestMapping("/get")
public User get() {
User user = userService.read(1L);
for (int i = 0; i < 10; i++) {
log.info("READ: {}", user);
user = userService.read(1L);
}
return user;
}
}
日志输出:
04:37:52.915 [io-8781-exec-10] READ: ID: 1 :: Joe
04:37:53.151 [nio-8781-exec-1] Entering update method for user 1
04:37:53.919 [io-8781-exec-10] READ: ID: 1 :: Joe
04:37:54.152 [nio-8781-exec-1] User 1 updated, falling asleep for 5s
04:37:54.922 [io-8781-exec-10] READ: ID: 1 :: Joe
04:37:55.926 [io-8781-exec-10] READ: ID: 1 :: Joe
04:37:56.932 [io-8781-exec-10] READ: ID: 1 :: Joe
04:37:57.937 [io-8781-exec-10] READ: ID: 1 :: Joe
04:37:58.943 [io-8781-exec-10] READ: ID: 1 :: Joe
04:37:59.222 [nio-8781-exec-1] UPDATED: ID: 1 :: Jack
04:37:59.947 [io-8781-exec-10] READ: ID: 1 :: Joe
04:38:00.950 [io-8781-exec-10] READ: ID: 1 :: Joe
04:38:01.956 [io-8781-exec-10] READ: ID: 1 :: Joe
控制器返回的响应也不同。
对于/update
,它是:
{"id":1,"username":"Jack"}
同时为/get
:
{"id":1,"username":"Joe"}
我正在使用MySQL 5.7.18和Spring Boot 2.1.0。
关于我可能做错/遗漏的任何想法? 预先感谢。
答案 0 :(得分:0)
您的代码可以与Oracle
一起正常工作,但与MySQL
可以有点不同。在此处阅读有关MySql中的隔离级别的信息
https://blog.pythian.com/understanding-mysql-isolation-levels-repeatable-read/
我相信,如果您将方法的isolation level
更改为Read committed
,则它应该可以正常工作,因为MySql默认具有Repeatable-read
,它的处理方式有所不同。
@Transactional(Isolation.READ_COMMITTED)
public User update(Long id, User update)
答案 1 :(得分:0)
设置:
@Override
@SneakyThrows
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
public User read(Long id) {
User user = userRepository.findById(id).get();
Thread.sleep(1000);
return user;
}