美文网首页
解决@Transactional不能跨线程池共享事务的问题—使用

解决@Transactional不能跨线程池共享事务的问题—使用

作者: 猫尾草 | 来源:发表于2021-12-24 15:57 被阅读0次

    1. @Transactional不能跨线程池共享事务

    两个加了@Transactional注解的数据库查询方法:

    1. TestTransactionalConsumerService.testDemoUser()
    2. TestTransactionalProviderService.testDemoUser()

    前者通过线程池调用后者。
    主要代码如下:
    TestTransactionalConsumerService:

    @Transactional
    public void testDemoUser() {
        Page<DemoUser> page = new Page<>(1, 10);
        IPage<DemoUser> userList = demoUserMapper.selectPage(page, new ueryWrapper<DemoUser>().lambda().eq(DemoUser::getAge, 24));
        userList.getRecords().forEach(System.out::println);
        CompletableFuture.runAsync(() -> testTransactionalProviderService.testDemoUser(), ttlThreadPool);
    }
    

    TestTransactionalProviderService:

    @Transactional
    public void testDemoUser() {
        Page<DemoUser> page = new Page<>(1, 10);
        IPage<DemoUser> userList = demoUserMapper.selectPage(page, new ueryWrapper<DemoUser>().lambda().eq(DemoUser::getAge, 18));
        userList.getRecords().forEach(System.out::println);
    }
    

    数据库查询 1 在主线程,数据库查询 2 是主线程在线程池中调用。
    MySQL日志如下(省略了了无关的一些日志):

    2021-10-06T13:15:03.723491Z    17 Query SET autocommit=0
    2021-10-06T13:15:03.847866Z    17 Query SELECT COUNT(*) AS total FROM demo_user WHERE (age = 24)
    2021-10-06T13:15:03.877622Z    17 Query SELECT  id,name,age,email  FROM demo_user WHERE (age = 24) LIMIT 10
    2021-10-06T13:15:03.907605Z    18 Query SET autocommit=1
    2021-10-06T13:15:03.907852Z    18 Query SET autocommit=0
    2021-10-06T13:15:03.921955Z    18 Query SELECT COUNT(*) AS total FROM demo_user WHERE (age = 18)
    2021-10-06T13:15:03.939692Z    18 Query SELECT  id,name,age,email  FROM demo_user WHERE (age = 18) LIMIT 10
    2021-10-06T13:15:03.941746Z    18 Query commit
    2021-10-06T13:15:03.942005Z    18 Query SET autocommit=1
    2021-10-06T13:15:05.912507Z    17 Query commit
    2021-10-06T13:15:05.912896Z    17 Query SET autocommit=1
    

    查询 1 和 查询 2 分别开启了一个事务,并不在同一个事务中。我们最开始对@Transactional注解理解不足时可能会误以为这两个查询在同一个事务中,从而出现预想之外的结果。

    2. 一种实验性的解决方法

    @Transactional注解中保存数据库连接使用的是NamedThreadLocal,下载 spring 的源码使用TransmittableThreadLocal代替ThreadLocal重写spring-core中的NamedThreadLocal类,重新打包后替换掉springboot工程中的spring-core详见这里:使用TransmittableThreadLocal替换Thread Local重新打包spring。同时使用ttl线程池替换普通线程池。MySQL日志如下:

    2021-10-06T13:16:22.207792Z    19 Query SET autocommit=0
    2021-10-06T13:16:22.335394Z    19 Query SELECT COUNT(*) AS total FROM demo_user WHERE (age = 24)
    2021-10-06T13:16:22.366691Z    19 Query SELECT  id,name,age,email  FROM demo_user WHERE (age = 24) LIMIT 10
    2021-10-06T13:16:22.433475Z    19 Query SELECT COUNT(*) AS total FROM demo_user WHERE (age = 18)
    2021-10-06T13:16:22.451351Z    19 Query SELECT  id,name,age,email  FROM demo_user WHERE (age = 18) LIMIT 10
    2021-10-06T13:16:24.409886Z    19 Query commit
    2021-10-06T13:16:24.410713Z    19 Query SET autocommit=1
    

    可以看到,两个查询使用的是同一个数据库事务。

    相关文章

      网友评论

          本文标题:解决@Transactional不能跨线程池共享事务的问题—使用

          本文链接:https://www.haomeiwen.com/subject/ekvbfrtx.html