背景,一个service,有一个方法serviceA里面调用两个update方法,一个findByName方法。其中serviceA和update方法上都上有@transactional注解,而findByName上没有。示例如下:
servcieA{
updateByName()
updateById()
findByName()
}
先说结论,这个时候具体事务的变化如下:
- serviceA本身会先创建一个事务
- updateByName获取service创建的事务
- updateById也获取service创建的事务
- findByName并不会获取事务,所以是没有事物的
- 方法结束serviceA提交该事务
下面是源码分析,如果贴一大段源码,再写一大段注释,看起来也很累,为了方便理解,也不记录代码细节,同样直接说结论。
类名:org.springframework.transaction.interceptor.TransactionAspectSupport
-
方法名:invokeWithinTransaction
该方法中的逻辑大致可以抽象为
-
读取事务配置:根据方法上,接口上,还有动态代理的实现类上是否有注解,来决定是否有事务配置
-
创建或者获取事务:如果没有事务配置,不会走这里
-
执行方法:反射调用原方法
-
提交事务:判断事务是否新建,如果不是的话,则不会提交
-
-
方法名:createTransactionIfNecessary
该方法根据名字就可以判断出来,如果需要就创建一个事务,如果不需要就获取当前事务,但是有几点需要解释下
- 在方法调用之前,ThreadLocal的一个map里面已经有一个对象
- 当serviceA被调用,会把这个对象取出来,isTransactionActive改为True,并且开启这个事务
- updateByName被调用,也会获取这个对象,这时候isTransactionActive已经是true,那么就不需要重新创建事务了
-
方法名:commitTransactionAfterReturning
该方法从名字也可以看出来,原方法returen之后去提交这个事务,也有几点说明
- 在我们背景设定下,当两个update方法执行之后,也会走到这里,但是由于status.isNewTransaction是false,并不会真的提交
- 而当serviceA方法返回时,status.isNewTransaction是true,才会走真的提交逻辑
这时候引入了一个新的问题,为什么findByName可以读取到update之后的数据?
这个暂时没看相关源码,所以先问下chatgpt,回头空了可以再看下源码,给出个确切的答案。
即使 `findByName` 方法没有标记 `@Transactional` 注解,它也能读取到更新后的数据,因为它是在**当前事务的上下文**中执行的。因为在同一个 `Persistence Context` 中,所有的数据变更和查询都共享同一个一级缓存:
1. **共享的事务上下文**:由于 `findByName` 是在同一个 `service` 实例内部调用的,即使没有 `@Transactional` 注解,Spring 也会将其纳入已有事务中,从而共享了当前的事务上下文。
2. **缓存的一致性**:一级缓存能够确保事务内的数据一致性,已经更新的数据会立刻反映在缓存中,因此 `findByName` 能查询到更新后的数据。
标签:事务,缓存,SpringJpa,serviceA,调用,方法,findByName
From: https://www.cnblogs.com/my-king/p/18502838