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     }

从上面代码可以看出,我们的解决方案是使用了编程式事务。

原文地址:https://www.cnblogs.com/xiaojiesir/p/11089142.html

时间: 2024-11-01 16:09:33

Spring中同一个service中方法相互调用事务不生效问题解决方案的相关文章

Spring中的@Service(&quot;dataDictionaryService&quot;)注解

1.接口 public interface DataDictionaryService { /** * 获取兑换率 * @param coinType 原币种 * @param billCoin 折币种 * @param date 取离该时间最近的兑换率 * @return */ public BigDecimal getExch(String coinType, String billCoin, Date date); } 如图,有两个实现类. 2.实现类 @Service("dataDict

main方法调用spring中dao service方法

public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); System.out.println("aaa"); // ServiceReportService service = (ServiceReportService)context.getBean(&quo

Spring中文文档-第一部分

一. Spring 框架概述 Spring是为了构建企业应用的轻量级框架.然而,Spring是模块化的,允许你只是使用其中的一部分,不需要引入其他的.你可以在任何web框架上使用IoC容器,也可以只使用Hibernate集成代码或JDBC抽象层.Spring框架支持声明式事务管理.通过RMI远程访问.WebService和持久化数据.它还提供全功能的MVC框架,和将AOP应用到你的系统上. Spring设计为非侵入式的,意味着你的逻辑代码不依赖与框架本身. 1. 开始使用Spring 学习Spr

Spring MVC普通类或工具类中调用service报空空指针的解决办法(调用service报java.lang.NullPointerException)

当我们在非Controller类中应用service的方法是会报空指针,如图: 这是因为Spring MVC普通类或工具类中调用service报空null的解决办法(调用service报java.lang.NullPointerException) 按上述步骤解决完自己的工具类后,你会发现项目运行后仍然报空指针此时你需要在applicationContext.xml 配置文件中添加一行配置文件 如图: 对自己工具类所在的包进行注解扫描,使Spring能够识别自己上面所配置的注解 原文地址:htt

同一个Controller里的同一个Service实例,在当前的Controller里的不同方法中状态不一致

直接上代码如下: @Controller@RequestMapping("/views/information")public class PubContentController extends BaseController{ @Autowired      private ContentCategoryService contentCategoryService; /**     * 新增资讯     *      * @param pubContent     * @param

Spring中 如果该Service有多个实现类,它怎么知道该注入哪个ServiceImpl类?

1.每个service的impl都可以指定名称(使用@Service(“名称”)) 2.Controller中注入service的时候使用名称来指定注入哪一个. (1). @Autowired @Qualifier("名称") (2). @Resource(name="名称") 代码如下: 接口 public interface HumanService { public String name();} 接口实现类 @Service("teacherSer

Spring中@Transactional事务回滚实例及源码

一.使用场景举例 在了解@Transactional怎么用之前我们必须要先知道@Transactional有什么用.下面举个栗子:比如一个部门里面有很多成员,这两者分别保存在部门表和成员表里面,在删除某个部门的时候,假设我们默认删除对应的成员.但是在执行的时候可能会出现这种情况,我们先删除部门,再删除成员,但是部门删除成功了,删除成员的时候出异常了.这时候我们希望如果成员删除失败了,之前删除的部门也取消删除.这种场景就可以使用@Transactional事物回滚. 二.checked异常和unc

Spring中的AOP(五)——在Advice方法中获取目标方法的参数

摘要: 本文介绍使用Spring AOP编程中,在增强处理方法中获取目标方法的参数,定义切点表达式时使用args来快速获取目标方法的参数. 获取目标方法的信息 访问目标方法最简单的做法是定义增强处理方法时,将第一个参数定义为JoinPoint类型,当该增强处理方法被调用时,该JoinPoint参数就代表了织入增强处理的连接点.JoinPoint里包含了如下几个常用的方法: Object[] getArgs:返回目标方法的参数 Signature getSignature:返回目标方法的签名 Ob

Spring 中 ApplicationContext 和 BeanFactory 的区别,以及 Spring bean 作用域

//从ApplicationContext 中取 bean ApplicationContext ac = new ClassPathXmlApplicationContext ( "com/hsp/beans.xml" ) ; ac.getBean("beanId"); 当我们去实例化beans.xml,该文件中配置的 bean 就被实例化(不论你用还是不用,bean对象都在那),而且该对象是singleton单例的.(每个bean都有scope属性,可以人为的设