@Transactional注解不生效原因及解决方案
今天再工作中遇到了一个问题:再插入数据前需要进行操作记录,因此再service层调用的保存操作记录的方法,考虑到操作记录不应被后面的操作所影响,因此将事务的传播型为设置为REQUIRES_NEW
(如果当前有事务存在,将当前事务挂起,重新开启新事务执行。事务的提交或回滚不受之前事务的影响)测试时发现即使设置了REQUIRES_NEW后保存操作记录的方法仍然回被后面的代码所影响,后面代码报错,操作记录的事务也被回滚了,原因是没有通过代理对象去调用方法,因此事务没有被代理对象处理@Transactional事务失效了。
模拟当时的场景:
数据库内有user1表,name的长度为5,serivice中有save和saveLog方法再save方法内调用saveLog方法。
create table user1(
id int primary key auto_increment,
name varchar(5),
create_date datetime
);
service:
@Service
public class User1Service {
@Autowired
private User1Mapepr user1Mapepr; //mybaits的mapper
//这里传播行为声明为REQUIRED(如果当前没事务,创建事务,否则加入到当前事务)
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void save(){
//先保存记录
saveLog();
//后保存用户(这里会报错,因为name字段长度超过数据库规定的5字符)
user1Mapepr.save(new User1(null,"zs1111111111",new Date()));
}
//传播行为声明为REQUIRES_NEW(不管当前是否有事务,都创建新事务运行)
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void saveLog(){
//这里保存用户,能正常执行
System.out.println("saveLog");
user1Mapepr.save(new User1(null,"s1",new Date()));
System.out.println("=======保存成功=======");
}
}
执行后发现,虽然事务传播行为为REQIREES_NEW但是并没有创建新事务去执行,原因是没有通过代理对象调用saveLog方法,因此@Transactional事务失效
解决办法:
通过AopContext获取当前对象的代理对象后调用saveLog方法
@Service
public class User1Service {
@Autowired
private User1Mapepr user1Mapepr;
@Autowired
private Service2 service2;
//这里传播行为声明为REQUIRED(如果当前没事务,创建事务,否则加入到当前事务)
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void save(){
//通过AopContext.currentProxy()获取当前对象的代理对象
User1Service aopService = (User1Service)AopContext.currentProxy();
//spring还为我们提供了AopUtils工具类
System.out.println("是否是aop对象===>"+AopUtils.isAopProxy(service));
System.out.println("是否是CGLIB===>"+AopUtils.isCglibProxy(service));
System.out.println("是否是JDK===>"+AopUtils.isJdkDynamicProxy(service));
//调用aop代理对象保存日志
aopService.saveLog();
user1Mapepr.save(new User1(null,"zs1111111111",new Date()));
}
//传播行为声明为REQUIRES_NEW(不管当前是否有事务,都创建新事务运行)
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void saveLog(){
//这里保存用户,能正常执行
System.out.println("saveLog");
user1Mapepr.save(new User1(null,"s1",new Date()));
System.out.println("=======保存成功=======");
}
}
执行后,即使后面保存用户报错,也不影响saveLog事务的执行,@Transactional能后正常执行