Springboot种声明式事务(@Transactional注解)不生效的场景

一、引进来

数据库操作中保证数据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方法内部用this的方法调用带事务的B方法的时候,B事务就是失效

解决方法:方法A和B都需要支持事务。

如果同类中A方法调用B方法,被调用方法B使用@Transactional(propagation = Propagation.REQUIRES_NEW)注解进行修饰REQUIRES_NEW表示新开一个事务),那么事务也不会生效

解决方法:

一个是可以在类中自己注入自己,用注入的对象调用另一个方法。

另一个是将同一个类中的两个事务方法拆分到两个类中,A方法所在的类中注入B方法所在的类。

6、数据源是否配置管理器

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

7、方法被final或者static修饰

方法使用了final或者static之后,不能被代理类重写,因此事务丢失.

你可能感兴趣的:(java开发,数据库类,java,spring,数据库开发,spring,boot)