Spring中同一个service中方法相互调用事务不生效问题解决方案
Posted xiaojiesir
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring中同一个service中方法相互调用事务不生效问题解决方案相关的知识,希望对你有一定的参考价值。
问题描述:
我们在用Spring框架开发Web项目过程中,经常需要用同一个service中的一个方法调用另一个方法,如果此时调用方没有添加事务注解@Transactional,而在被调用方添加事务注解@Transactional,当被调用方法中出现异常,这时候会发现事务并没有回滚,事务注解@Transactional没有起作用。
分析原因:
我们知道Spring中事务管理是使用AOP代理技术实现的,目标对象自身并没有事务管理功能的,而是通过代理对象动态增强功能对事务进行增强的。因此当我们在同一个service类中通过一个方法调用另一个方法时,是通过目标对象this对象调用的,目标对象自身并没有事务管理功能,因此事务不能生效。
下面我们用代码演示下:
1 public class UserService 2 ... 3 public User getUserByName(String name) 4 return userDao.getUserByName(name); 5 6 ...
如果配置了事务, 就相当于又创建了一个类:
1 public class UserServiceProxy extends UserService 2 private UserService userService; 3 ... 4 public User getUserByName(String name) 5 User user = null; 6 try 7 // 在这里开启事务 8 user = userService.getUserByName(name); 9 // 在这里提交事务 10 11 catch(Exception e) 12 // 在这里回滚事务 13 14 // 这块应该需要向外抛异常, 否则我们就无法获取异常信息了. 15 // 至于方法声明没有添加异常声明, 是因为覆写方法, 异常必须和父类声明的异常"兼容". 16 // 这块应该是利用的java虚拟机并不区分普通异常和运行时异常的特点. 17 throw e; 18 19 return user; 20 21 ... 22
1 @Autowired 2 private UserService userService; // 这里spring注入的实际上是UserServiceProxy的对象 3 4 private void test() 5 // 由于userService是UserServiceProxy的对象, 所以拥有了事务管理的能力 6 userService.getUserByName("aa"); 7
Spring事务失效的其他原因
通过对Spring事务代理模式的分析,我们不难发现Spring事务失效的原因有以下几种情况:
1.private、static、final的使用
解决方法:不在类和方法上使用此类关键字
2.通过this.xxx(调用当前类的方法)
使用xml配置方式暴露代理对象.然后在service中通过代理对象AopContext.currentProxy()去调用方法。
xml配置
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
service调用
1 @Service 2 public class HelloWorldServiceImpl implements HelloWorldService 3 @Autowired 4 private BlogRepository blogRepository; 5 6 @Override 7 public void a(BlogEntity blogEntity) throws Exception 8 ((HelloWorldService) AopContext.currentProxy()).b(blogEntity); 9 10 11 @Transactional(rollbackFor = Exception.class) 12 @Override 13 public void b(BlogEntity blogEntity) throws Exception 14 blogRepository.save(blogEntity); 15 throw new Exception("错误"); 16 17
3.使用默认的事务处理方式
spring的事务默认是对RuntimeException进行回滚,而不继承RuntimeException的不回滚。因为在java的设计中,它认为不继承RuntimeException的异常是”checkException”或普通异常,如IOException,这些异常在java语法中是要求强制处理的。对于这些普通异常,spring默认它们都已经处理,所以默认不回滚。可以添加rollbackfor=Exception.class来表示所有的Exception都回滚。
4.线程Thread中声明式事务不起作用
1 @Override 2 public void run() 3 DefaultTransactionDefinition def = new DefaultTransactionDefinition(); 4 def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); 5 PlatformTransactionManager txManager = ContextLoader.getCurrentWebApplicationContext().getBean(PlatformTransactionManager.class); 6 TransactionStatus status = txManager.getTransaction(def); 7 try 8 testDao.save(entity); 9 txManager.commit(status); // 提交事务 10 catch (Exception e) 11 System.out.println("异常信息:" + e.toString()); 12 txManager.rollback(status); // 回滚事务 13 14
从上面代码可以看出,我们的解决方案是使用了编程式事务。
以上是关于Spring中同一个service中方法相互调用事务不生效问题解决方案的主要内容,如果未能解决你的问题,请参考以下文章
独立的main方法调用Spring MVC框架中的service
java 怎么在serviceimpl中调用service的方法