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