场景如下:
1.springboot的启动类上没有加事务开启注解导致失效
@SpringBootApplication
@MapperScan("com.example.demo.dao")
@EnableSwagger2
@EnableTransactionManagement // 开启事务注解
@EnableAspectJAutoProxy(exposeProxy = true) // SpringAOP的功能
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2.mysql数据库使用的存储引擎不支持事务(MyISAM(不支持事务),InnoDB(支持事务))
即 确定你的数据库引擎是 InnoDB 下图可以看到Transactions列只有InnoDB支持事务
1、查看mysql支持的引擎有哪些
show engines
2、查看当前默认的引擎
show variables like 'default_storage_engine'
3、查看指定表当前的引擎,有2种方式
show table status where NAME ='t_user'
或
show create table t_user -- 在显示结果里参数engine后面的就表示该表当前用的存储引擎
4、修改指定表的引擎
alter table t_user engine=innodb;
5、修改mysql默认的数据库引擎
打开配置文件my.ini,将“default-storage-engine=MYISAM”改为你想设定的,然后重启即可
3.@Transactional注解的要是public方法
其实这个不怎么会忘记,使用idea的集成开发环境如果你注解一个私有的方法直接就有提示
4.@Transactional(rollbackFor = {RuntimeException.class, Exception.class})
回滚的异常要注意写对,内部代码被try{}catch(){}也不会回滚。。。。。。。
5.同一个类,内部不带事务的方法调用该类中带事务的方法,不会回滚。
原因:JDK的动态代理。只有被动态代理直接调用时才会产生事务。在SpringIoC容器中返回的调用的对象是代理对象而不是真实的对象。
解决办法:
方法1、在方法A上开启事务,方法B不用事务或默认事务,并在方法A的catch中throw new RuntimeException();(在没指定rollbackFor时,默认回滚的异常为RuntimeException),这样使用的就是方法A的事务。(一定要throw new RuntimeException();否则异常被捕捉处理,同样不会回滚。)如下:
@Transactional() //开启事务
public void save(){
try {
this.saveEmployee(); //这里this调用会使事务失效,数据会被保存
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException();
}
}
方法2、方法A上可以不开启事务,方法B上开启事务,并在方法A中将this调用改成动态代理调用(AopContext.currentProxy()),如下:
public void save(){
try {
EmployeeService proxy =(EmployeeService) AopContext.currentProxy();
proxy.saveEmployee();
}catch (Exception e){
e.printStackTrace();
}
}
注意
1.这个方法需要启动类上开启aop的配置 即加 @EnableAspectJAutoProxy(exposeProxy = true) 注解
2.添加依赖
<!--配合 @EnableAspectJAutoProxy(exposeProxy = true)-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
方法3、修改类,不要出现“自调用”的情况:这是Spring文档中推荐的“最佳”方案;
即重新写一个类,这样就避免自调用的this问题,事务就可以起作用了。