Spring中同一个service中方法相互调用事务不生效问题解决方案
问题描述:
我们在用Spring框架开发Web项目过程中,经常需要用同一个service中的一个方法调用另一个方法,如果此时调用方没有添加事务注解@Transactional,而在被调用方添加事务注解@Transactional,当被调用方法中出现异常,这时候会发现事务并没有回滚,事务注解@Transactional没有起作用。
分析原因:
我们知道Spring中事务管理是使用AOP代理技术实现的,目标对象自身并没有事务管理功能的,而是通过代理对象动态增强功能对事务进行增强的。因此当我们在同一个service类中通过一个方法调用另一个方法时,是通过目标对象this对象调用的,目标对象自身并没有事务管理功能,因此事务不能生效。
下面我们用代码演示下:
public class UserService{
...
public User getUserByName(String name) {
return userDao.getUserByName(name);
}
...
如果配置了事务, 就相当于又创建了一个类:
public class UserServiceProxy extends UserService{
private UserService userService;
...
public User getUserByName(String name){
User user = null;
try{
// 在这里开启事务
user = userService.getUserByName(name);
// 在这里提交事务
}
catch(Exception e){
// 在这里回滚事务 // 这块应该需要向外抛异常, 否则我们就无法获取异常信息了.
// 至于方法声明没有添加异常声明, 是因为覆写方法, 异常必须和父类声明的异常"兼容".
// 这块应该是利用的java虚拟机并不区分普通异常和运行时异常的特点.
throw e;
}
return user;
}
...
}
@Autowired
private UserService userService; // 这里spring注入的实际上是UserServiceProxy的对象 private void test(){
// 由于userService是UserServiceProxy的对象, 所以拥有了事务管理的能力
userService.getUserByName("aa");
}
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调用
@Service
public class HelloWorldServiceImpl implements HelloWorldService {
@Autowired
private BlogRepository blogRepository; @Override
public void a(BlogEntity blogEntity) throws Exception {
((HelloWorldService) AopContext.currentProxy()).b(blogEntity);
} @Transactional(rollbackFor = Exception.class)
@Override
public void b(BlogEntity blogEntity) throws Exception {
blogRepository.save(blogEntity);
throw new Exception("错误");
}
}
3.使用默认的事务处理方式
spring的事务默认是对RuntimeException进行回滚,而不继承RuntimeException的不回滚。因为在java的设计中,它认为不继承RuntimeException的异常是”checkException”或普通异常,如IOException,这些异常在java语法中是要求强制处理的。对于这些普通异常,spring默认它们都已经处理,所以默认不回滚。可以添加rollbackfor=Exception.class来表示所有的Exception都回滚。
4.线程Thread中声明式事务不起作用
@Override
public void run() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
PlatformTransactionManager txManager = ContextLoader.getCurrentWebApplicationContext().getBean(PlatformTransactionManager.class);
TransactionStatus status = txManager.getTransaction(def);
try {
testDao.save(entity);
txManager.commit(status); // 提交事务
} catch (Exception e) {
System.out.println("异常信息:" + e.toString());
txManager.rollback(status); // 回滚事务
}
}
从上面代码可以看出,我们的解决方案是使用了编程式事务。
5.配置的事务与扫描的service不在同一个容器
在spring-framework-reference.pdf文档中有这样一段话:
<tx:annotation-driven/> only looks for @Transactional on beans in the same application context it is defined in. This means that, if you put <tx:annotation-driven/> in a WebApplicationContext for a DispatcherServlet, it only checks for @Transactional beans in your controllers, and not your services.
这句话的意思是,<tx:annoation-driven/>只会查找和它在相同的应用上下文件中定义的bean上面的@Transactional注解,如果你把它放在Dispatcher的应用上下文中,它只检查控制器上的@Transactional注解,而不是你services上的@Transactional注解。
如果将事务配置定义在Spring MVC的应用上下文(*-servlet.xml)中,在Controller上的@Transactional注解是可以起作用的。而services上的@Transactional注解将不起作用。
详情可见:https://www.cnblogs.com/xiaojiesir/p/11058541.html
6.方法配置的事务传播行为有问题
被调用方法的事务传播行为设置为PROPAGATION_REQUIRES_NEW,导致产生两个独立的事务,外围方法抛出异常只回滚和外围方法同一事务的方法。
详情可见:https://segmentfault.com/a/1190000013341344
Spring中同一个service中方法相互调用事务不生效问题解决方案的更多相关文章
- Spring中的Service/DAO/DTO
- 详谈 Spring 中的 IOC 和 AOP
这篇文章主要讲 Spring 中的几个点,Spring 中的 IOC,AOP,下一篇说说 Spring 中的事务操作,注解和 XML 配置. Spring 简介 Spring 是一个开源的轻量级的企业 ...
- Spring中文文档-第一部分
一. Spring 框架概述 Spring是为了构建企业应用的轻量级框架.然而,Spring是模块化的,允许你只是使用其中的一部分,不需要引入其他的.你可以在任何web框架上使用IoC容器,也可以只使 ...
- 面试官:说说Spring中的事务传播行为
前言 在开发中,相信大家都使用过Spring的事务管理功能.那么,你是否有了解过,Spring的事务传播行为呢? Spring中,有7种类型的事务传播行为.事务传播行为是Spring框架提供的一种事务 ...
- Spring MVC普通类或工具类中调用service报空空指针的解决办法(调用service报java.lang.NullPointerException)
当我们在非Controller类中应用service的方法是会报空指针,如图: 这是因为Spring MVC普通类或工具类中调用service报空null的解决办法(调用service报java.la ...
- Spring框架中,在工具类或者普通Java类中调用service或dao
spring注解的作用: 1.spring作用在类上的注解有@Component.@Responsity.@Service以及@Controller:而@Autowired和@Resource是用来修 ...
- Spring中 如果该Service有多个实现类,它怎么知道该注入哪个ServiceImpl类?
方法一:Controller中注入service的时候使用@Autowired自动注入,@Qualifier("beanId") 来指定注入哪一个. 方法二:Controller中 ...
- spring 事务处理中,同一个类中:A方法(无事务)调B方法(有事务),事务不生效问题
public class MyEntry implements IBaseService{ public String A(String jsonStr) throws Exception{ User ...
- Spring中,多个service发生嵌套,事务是怎么样的?
前言 最近在项目中发现了一则报错:"org.springframework.transaction.UnexpectedRollbackException: Transaction roll ...
随机推荐
- windows服务器下,mysql运行一段时间之后忽然无法连接,但是mysql服务启动正常
出现这种情况以前都是重启服务器可以解决,但是治标不治本,一段时间之后仍然会出现此问题. 此问题不是mysql应用程序的问题而是windows server system 的配置问题.因此需要修改win ...
- MySQL 偶尔抽风,性能突然下降
有时会碰到这样的情况,一条 SQL 在平时执行没问题,很快.但是突然某个时间执行的就会很慢,而且这种场景并不能复现,只能随机发送的. SQL 执行突然变慢的原因 在之前讲解 MySQL Redo lo ...
- 群晖系统设置自动拍摄共享文件夹快照的教程【江东网 JDX86.COM】
Snapshot Replication 是数据备份和还原的工具.企业需要数据保护以防止因意外删除.应用程序崩溃.数据损毁和病毒所造成的数据丢失. 1.在套件中心下载该套件 2.打开套件可以看到NAS ...
- eclipse git如何切换分支,拉取代码,合并代码,解决冲突等
(如果想看eclipse拉取git项目,移步到我上一篇文章)以下步骤是eclipse运用git的切换分支,拉取合并代码的基本操作: 1.切换远程分支:鼠标右键项目--team--switch to - ...
- 弱校验之@NotNull@NotEmpty@NotBlank
@NotNull 适用于非空判断 The annotated element must not be {@code null}. CharSequence, Collection, Map 和 Arr ...
- 第6篇scrum冲刺(5.26)
一.站立会议 1.照片 2.工作安排 成员 昨天已完成的工作 今天的工作安排 困难 陈芝敏 研究云开发,更新了登录模块,把用户的信息传入数据库了 学习云开发,云函数调用以及数据的前后端传递 遇 ...
- 图数据库对比:Neo4j vs Nebula Graph vs HugeGraph
本文系腾讯云安全团队李航宇.邓昶博撰写 图数据库在挖掘黑灰团伙以及建立安全知识图谱等安全领域有着天然的优势.为了能更好的服务业务,选择一款高效并且贴合业务发展的图数据库就变得尤为关键.本文挑选了几款业 ...
- 一台主机的最大TCP连接数是多少?
在没接触过这个问题之前,自然会想到服务器端连接数是由服务器端口号限制的.但这其实是一个很严重的误解,要解决这个问题,必须理解socket的连接过程. 以python为例,tcp服务端socket需要经 ...
- java里equals和hashCode之间什么关系
如果要比较实际内存中的内容,那就要用equals方法,但是!!! 如果是你自己定义的一个类,比较自定义类用equals和==是一样的,都是比较句柄地址,因为自定义的类是继承于object,而objec ...
- sublime Text 3安装 Sublime Package Control(这个可以用于安装各种插件)时显示默认安装的c盘内存不够的解决方案
首先先关闭st3 之后在安装的路径下创建Data文件夹,然后打开st3(sublime Text 3简写)CTRL+`打开命令行输入以下内容,直接回车,等待下载完成就可以了import urllib. ...