Spring系列第51篇:导致 Spring 事务失效常见的几种情况
这算是spring事务第9篇文章了,花了这么多篇文章介绍事务这块的知识,说明事务这块的东西确实比较多、知识点比较细,也非常重要,希望大家能够重视起来,吃透这块的知识。
本文2个目的:
1、使用spring事务的过程中,哪些情况会导致事务失效?
2、遇到事务相关bug时,有哪些方法可以快速定位bug?
3、文末有福利
1、事务失效的7种情况
- 未启用spring事务管理功能
- 方法不是public类型的
- 数据源未配置事务管理器
- 自身调用问题
- 异常类型错误
- 异常被吞了
- 业务和spring事务代码必须在一个线程中
1.1、未启用spring事务管理功能
@EnableTransactionManagement 注解用来启用spring事务自动管理事务的功能,这个注解千万不要忘记写了。
1.2、方法不是public类型的
@Transaction 可以用在类上、接口上、public方法上,如果将@Trasaction用在了非public方法上,事务将无效。
1.3、数据源未配置事务管理器
spring是通过事务管理器了来管理事务的,一定不要忘记配置事务管理器了,要注意为每个数据源配置一个事务管理器:
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
1.4、自身调用问题
spring是通过aop的方式,对需要spring管理事务的bean生成了代理对象,然后通过代理对象拦截了目标方法的执行,在方法前后添加了事务的功能,所以必须通过代理对象调用目标方法的时候,事务才会起效。
看下面代码,大家思考一个问题:当外部直接调用m1的时候,m2方法的事务会生效么?
@Component
public class UserService {
public void m1(){
this.m2();
}
@Transactional
public void m2(){
//执行db操作
}
}
显然不会生效,因为m1中通过this的方式调用了m2方法,而this并不是代理对象,this.m2()不会被事务拦截器,所以事务是无效的,如果外部直接调用通过UserService这个bean来调用m2方法,事务是有效的,上面代码可以做一下调整,如下,@1在UserService中注入了自己,此时m1中的m2事务是生效的
@Component
public class UserService {
@Autowired //@1
private UserService userService;
public void m1() {
this.userService.m2();
}
@Transactional
public void m2() {
//执行db操作
}
}
重点:必须通过代理对象访问方法,事务才会生效。
1.5、异常类型错误
spring事务回滚的机制:对业务方法进行try catch,当捕获到有指定的异常时,spring自动对事务进行回滚,那么问题来了,哪些异常spring会回滚事务呢?
并不是任何异常情况下,spring都会回滚事务,默认情况下,RuntimeException和Error的情况下,spring事务才会回滚。
也可以自定义回滚的异常类型:
@Transactional(rollbackFor = {异常类型列表})
1.6、异常被吞了
当业务方法抛出异常,spring感知到异常的时候,才会做事务回滚的操作,若方法内部将异常给吞了,那么事务无法感知到异常了,事务就不会回滚了。
如下代码,事务操作2发生了异常,但是被捕获了,此时事务并不会被回滚
@Transactional
public void m1(){
事务操作1
try{
事务操作2,内部抛出了异常
}catch(Exception e){
}
}
1.7、业务和spring事务代码必须在一个线程中
spring事务实现中使用了ThreadLocal,ThreadLocal大家应该知道吧,可以实现同一个线程中数据共享,必须是同一个线程的时候,数据才可以共享,这就要求业务代码必须和spring事务的源码执行过程必须在一个线程中,才会受spring事务的控制,比如下面代码,方法内部的子线程内部执行的事务操作将不受m1方法上spring事务的控制,这个大家一定要注意
@Transactional
public void m1() {
new Thread() {
一系列事务操作
}.start();
}
2、如何快速定位事务相关bug?
2种方式
方式1:看日志
如果你使用了logback或者log4j来输出日志,可以修改一下日志级别为debug模式,可以看到事务的详细执行日志,帮助你定位错误
方式2:调试代码
如果你对源码比较了解,那么你会知道被spring管理事务的业务方法,执行的时候都会被TransactionInterceptor拦截器拦截,会进入到它的invoke方法中,咱们可以在invoke方法中设置一些断点,可以看到详细的执行过程,排错也就比较容易了。
整体上来说,还是需要你深入理解原理,原理了解了,写代码的时候本身就会避免很多坑。
3、Spring系列
- Spring系列第1篇:为何要学spring?
- Spring系列第2篇:控制反转(IoC)与依赖注入(DI)
- Spring系列第3篇:Spring容器基本使用及原理
- Spring系列第4篇:xml中bean定义详解(-)
- Spring系列第5篇:创建bean实例这些方式你们都知道?
- Spring系列第6篇:玩转bean scope,避免跳坑里!
- Spring系列第7篇:依赖注入之手动注入
- Spring系列第8篇:自动注入(autowire)详解,高手在于坚持
- Spring系列第9篇:depend-on到底是干什么的?
- Spring系列第10篇:primary可以解决什么问题?
- Spring系列第11篇:bean中的autowire-candidate又是干什么的?
- Spring系列第12篇:lazy-init:bean延迟初始化
- Spring系列第13篇:使用继承简化bean配置(abstract & parent)
- Spring系列第14篇:lookup-method和replaced-method比较陌生,怎么玩的?
- Spring系列第15篇:代理详解(Java动态代理&cglib代理)?
- Spring系列第16篇:深入理解java注解及spring对注解的增强(预备知识)
- Spring系列第17篇:@Configration和@Bean注解详解(bean批量注册)
- Spring系列第18篇:@ComponentScan、@ComponentScans详解(bean批量注册)
- Spring系列第18篇:@import详解(bean批量注册)
- Spring系列第20篇:@Conditional通过条件来控制bean的注册
- Spring系列第21篇:注解实现依赖注入(@Autowired、@Resource、@Primary、@Qulifier)
- Spring系列第22篇:@Scope、@DependsOn、@ImportResource、@Lazy 详解
- Spring系列第23篇:Bean生命周期详解
- Spring系列第24篇:父子容器详解
- Spring系列第25篇:@Value【用法、数据来源、动态刷新】
- Spring系列第26篇:国际化详解
- Spring系列第27篇:spring事件机制详解
- Spring系列第28篇:Bean循环依赖详解
- Spring系列第29篇:BeanFactory扩展(BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor)
- Spring系列第30篇:jdk动态代理和cglib代理
- Spring系列第31篇:aop概念详解
- Spring系列第32篇:AOP核心源码、原理详解
- Spring系列第33篇:ProxyFactoryBean创建AOP代理
- Spring系列第34篇:@Aspect中@Pointcut 12种用法
- Spring系列第35篇:@Aspect中5中通知详解
- Spring系列第36篇:@EnableAspectJAutoProxy、@Aspect中通知顺序详解
- Spring系列第37篇:@EnableAsync & @Async 实现方法异步调用
- Spring系列第38篇:@Scheduled & @EnableScheduling定时器详解
- Spring系列第39篇:强大的Spel表达式
- Spring系列第40篇:缓存使用(@EnableCaching、@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig)
- Spring系列第41篇:@EnableCaching集成redis缓存
- Spring系列第42篇:玩转JdbcTemplate
- Spring系列第43篇:spring中编程式事务怎么用的?
- Spring系列第44篇:详解spring声明式事务(@Transactional)
- Spring系列第45篇:带你吃透Spring事务7种传播行为
- Spring系列第46篇:Spring如何管理多数据源事务?
- Spring系列第47篇:spring编程式事务源码解析
- Spring系列第48篇:@Transaction 事务源码解析
- Spring系列第49篇:通过Spring事务实现MQ中的事务消息
- Spring系列第50篇:spring事务拦截器顺序如何控制?
- Spring系列第51篇:导致 Spring 事务失效常见的几种情况
4、更多好文章
- Java高并发系列(共34篇)
- MySql高手系列(共27篇)
- Maven高手系列(共10篇)
- Mybatis系列(共12篇)
- 聊聊db和缓存一致性常见的实现方式
- 接口幂等性这么重要,它是什么?怎么实现?
- 泛型,有点难度,会让很多人懵逼,那是因为你没有看这篇文章!