1. @Transactional不能跨線程池共享事務(wù)
兩個(gè)加了@Transactional
注解的數(shù)據(jù)庫查詢方法:
TestTransactionalConsumerService.testDemoUser()
TestTransactionalProviderService.testDemoUser()
前者通過線程池調(diào)用后者村刨。
主要代碼如下:
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);
}
數(shù)據(jù)庫查詢 1 在主線程症脂,數(shù)據(jù)庫查詢 2 是主線程在線程池中調(diào)用。
MySQL日志如下(省略了了無關(guān)的一些日志):
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 分別開啟了一個(gè)事務(wù)侣诺,并不在同一個(gè)事務(wù)中。我們最開始對(duì)@Transactional
注解理解不足時(shí)可能會(huì)誤以為這兩個(gè)查詢?cè)谕粋€(gè)事務(wù)中谬运,從而出現(xiàn)預(yù)想之外的結(jié)果离福。
2. 一種實(shí)驗(yàn)性的解決方法
@Transactional
注解中保存數(shù)據(jù)庫連接使用的是NamedThreadLocal
,下載 spring 的源碼使用TransmittableThreadLocal
代替ThreadLocal
重寫spring-core
中的NamedThreadLocal
類澡屡,重新打包后替換掉springboot
工程中的spring-core
猿挚。詳見這里:使用TransmittableThreadLocal替換Thread Local重新打包spring。同時(shí)使用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
可以看到绩蜻,兩個(gè)查詢使用的是同一個(gè)數(shù)據(jù)庫事務(wù)。