繁体   English   中英

Spring Data JPA:具有隔离 READ_COMMITTED 的事务看不到另一个事务中提交的数据

[英]Spring Data JPA : A transaction with isolation READ_COMMITTED does not see data committed in another transaction

所以,我有一个非常简单的基于 Spring Boot 的 Web 应用程序。

数据库有一个表user ,列idusername以及一条记录: (1, 'Joe')

我还有以下课程:

User - 映射到表user的实体

UserRepository - 它的 Spring Data JPA 存储库

UserService + DefaultUserService - 具有 CRUD 方法的服务层

UserController - 具有两种方法的控制器: getupdate

Application - 主类(带有@EnableTransactionManagement注释)

所以,我想要做的是测试事务隔离级别READ_COMMITTED 我同时发送两个请求:

  1. /update ,它更新用户,将其名称设置为Jack ,然后让当前线程休眠 5 秒,然后提交事务。
  2. /get ,它重复读取同一用户 10 次,每次尝试后小睡 1 秒。

问题是,即使在 (1) 的事务已经提交之后,(2) 仍然返回一个旧值 - Joe 如果我在此之后尝试向/get发送另一个请求,它会按预期返回Jack ,因此仅当事务 (2) 在事务 (1) 提交对数据库的更改之前已启动时才会发生此问题。

您可能会在下面找到一些代码以供参考。

服务:

@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。

关于我可能做错/遗漏的任何想法? 提前致谢。

您的代码与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) 

放:

@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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM