一、引进来
数据库操作中保证数据ACID特性的行为。(ACID:原子性、一致性、隔离性、持久性)
spring的事务有两种:编程式事务和声明式事务。其中:
编程式事务:指通过代码实现事务管理
声明式事务:xml方式配置、方法或类中加@Transactional注解(springboot项目中会自动配置DataSourceTransactional到spring)
二、讲道理
1、判断数据库引擎支不支持事务
例如
MySQL
数据库,
其 MyISAM 引擎是不支持事务操作的,InnoDB
和NDB
才是支持事务的引擎,一般要支持事务都会使用 InnoDB
2、抛出异常类型错误时
Spring的事务管理默认是针对Error异常和RuntimeException异常以及其子类进行事务回滚。
如果代码抛出的异常在这两中范围之外,则不可回滚。
例如:
抛出个
RuntimeException
的子类异常,NullPointerException可以回滚;
抛出个这两中范围之外的异常,像
SQLTimeoutException()
则不会回滚。
处理这种情况:
只需在
@Transaction注解里面指定回滚异常类型即可
,
如下示例:
@Transactional(rollbackFor = Exception.class)
3、在执行事物的方法时,如果对异常进行抛出,并且手动捕获了这个异常try…catch
在需要执行的sercvice里面不应该主动捕获异常,这会导致事物不生效,应该继续往上抛,在controller层捕获即可,这样事物也生效了,异常也捕获了。
如果确实需要try…catch可以在catch中加入手动回滚动作,示例如下:
try{
jdbcTemplate.update( updateSql)
}catch(Exception e){
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
e.printStackTrace();
}
4、方法名是否为pubic
@Transaction注解只对方法名为pubic的才生效,其他事物不会生效。因为只有@Transaction注解只有被其他方法调用才生效的,能被其他方法调用的方法,只能是public。
因为在SpringAOP的代理处理方式中:事务拦截器TransactionInterceptor会在目标方法执行前后进行拦截,动态通知拦截器DynamicAdvisedInterceptor(CglibAopProxy的内部类)的intercept方法或者JdkDynamicAopProxy的invoke方法会间接的调用AbstractFallbackTransactionAttributeSource的computeTransactionAttribute方法,获取Transaction注解的事务配置信息。
如果目标方法的修饰符不是public,computeTransactionAttribute方法返回null。
5、内部调用的2种场景
因为
@Transactional
要生效
,
需要经过Spring的代理类。所以
只有来自外部的方法调用被AOP代理
成功
捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为
。
自身调用
失效
,
即
该类
的
方法A没有
经过 Spring 的代理类
,
调
了
该类自己的方法
B
。
默认只有在外部调用事务才会生效
如下两种场景:
|
解决方法: 一个是可以在类中自己注入自己,用注入的对象调用另一个方法。 另一个是将同一个类中的两个事务方法拆分到两个类中,A方法所在的类中注入B方法所在的类。 |
6、数据源是否配置管理器
Springboot项目一般在启动类中开启事务管理即可
如下:
@EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
@SpringBootApplication
public class ProfiledemoApplication {
public static void main(String[] args) {
SpringApplication.run(ProfiledemoApplication.class, args);
}
}
如果需要指定使用那个事务管理器:
可参考这篇文章:https://www.cnblogs.com/weiwuxian-95/p/10429538.html
7、方法被final或者static修饰
方法使用了
final
或者
static
之后,不能被代理类重写,因此事务丢失.