Spring 异步任务事务回滚问题

不忍直视 2020-04-21 11:56:55
我在项目的A类中,使用线程池异步调用 了B类的方法,在B类方法中调用了C类的一个方法,然后再C类中调用了一个SQL查询方法。在运行过程中C类中调用的这个查询方法抛出了异常,我在C类的方法中并没处理,但是再B类调用C类的方法处,进行了try catch(Exception),异常被捕获到了,然后我在B类中更新了数据库,等 B类方法执行完后,控制台提示我,异步调用B类的线程发生了事务回滚,请问大神们这是为什么呀?
示意图如下:



然后B类的方法执行完成之后,控制台就提示我事务被回滚了
...全文
3427 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_40841539 2020-04-27
  • 打赏
  • 举报
回复
可以啊,点赞了
xiaoxiangqing 2020-04-27
  • 打赏
  • 举报
回复
不忍直视 2020-04-23
  • 打赏
  • 举报
回复
结贴了,感谢各位大佬,已经找到原因了,原来是C没有用到spring的代理对象,导致在C上面的@Transactional注解无效,每一次B调用C的方法,D都会将自己的只读事务加到B的事务中,因此D的事务报错,会导致B的事务回滚,感谢各位大佬
luj_1768 2020-04-23
  • 打赏
  • 举报
回复
你认为事务链该如何设计规则?事务链对事务包中的异常应该如何检测以及应该如何响应? 我觉得你所使用的系统的相关设计是无可挑剔的。
sotondolphin 2020-04-22
  • 打赏
  • 举报
回复
引用 6 楼 不忍直视 的回复:
引用 5 楼 sotondolphin 的回复:
A 上面的事务管理是没用的,Spring不支持跨线程的事务管理
我明白你的意思,你是说B方法里面抛出非受检异常时会回滚,但是我不是try catch捕获了么,讲道理的话,spring对捕获了的异常不会造成事务回滚才对吧?
@Transactional 只要是发生了运行时异常,就会回滚,和你是否捕获没有关系。
不忍直视 2020-04-22
  • 打赏
  • 举报
回复
引用 5 楼 sotondolphin 的回复:
A 上面的事务管理是没用的,Spring不支持跨线程的事务管理
我明白你的意思,你是说B方法里面抛出非受检异常时会回滚,但是我不是try catch捕获了么,讲道理的话,spring对捕获了的异常不会造成事务回滚才对吧?
sotondolphin 2020-04-22
  • 打赏
  • 举报
回复
A 上面的事务管理是没用的,Spring不支持跨线程的事务管理
不忍直视 2020-04-22
  • 打赏
  • 举报
回复
引用 2 楼 sotondolphin的回复:
By default, a transaction will be rolling back on RuntimeException and Error but not on checked exceptions (business exceptions). See DefaultTransactionAttribute.rollbackOn(Throwable) for a detailed explanation. 在B方法上加了@Transactional 注解,它默认有RuntimeException 或Error的 时候会自动回滚,所以在catch 里面的UPDATE执行时就会有异常抛出。这种在catch里面做update的调用也很奇葩,会有很大隐患。
不对不对,说错了,我A类是异步调用的B类呀,讲道理的话,A跟B不在同一个事务没才对呀
sotondolphin 2020-04-22
  • 打赏
  • 举报
回复
By default, a transaction will be rolling back on RuntimeException and Error but not on checked exceptions (business exceptions). See DefaultTransactionAttribute.rollbackOn(Throwable) for a detailed explanation. 在B方法上加了@Transactional 注解,它默认有RuntimeException 或Error的 时候会自动回滚,所以在catch 里面的UPDATE执行时就会有异常抛出。这种在catch里面做update的调用也很奇葩,会有很大隐患。
maradona1984 2020-04-22
  • 打赏
  • 举报
回复
引用 11 楼 不忍直视 的回复:
但是我在C上面并没有加Trasanctional注解,讲道理,C出现异常之后不应该将B的事务状态设置为回滚吧
有做声明式事务?
不忍直视 2020-04-22
  • 打赏
  • 举报
回复
但是我在C上面并没有加Trasanctional注解,讲道理,C出现异常之后不应该将B的事务状态设置为回滚吧
不忍直视 2020-04-22
  • 打赏
  • 举报
回复
引用 9 楼 maradona1984的回复:
或者C不加注解或者声明式事务,或者C的事务传播机制选择PAOPAGATION_REQUIRE_NEW(这个没试过,你可以尝试下)
这个我试过了,您可以看我的图中C就是没有加注解的,在删除C注解之前,我也尝试过PAOPAGATION_REQUIRE_NEW这个配置,但B中的事务仍然被回滚了,我是看过您其他的回答,然后跟我这个一对比感觉不一致呀
maradona1984 2020-04-22
  • 打赏
  • 举报
回复
或者C不加注解或者声明式事务,或者C的事务传播机制选择PAOPAGATION_REQUIRE_NEW(这个没试过,你可以尝试下)
maradona1984 2020-04-22
  • 打赏
  • 举报
回复
引用 6 楼 不忍直视 的回复:
引用 5 楼 sotondolphin 的回复:
A 上面的事务管理是没用的,Spring不支持跨线程的事务管理
我明白你的意思,你是说B方法里面抛出非受检异常时会回滚,但是我不是try catch捕获了么,讲道理的话,spring对捕获了的异常不会造成事务回滚才对吧?
你要理解spring事务的实现原理才能理解这个问题. 我已经回复过很多类似问题了,你可以搜索下. 简单来说,C抛异常,方法的aop会捕获异常,并标记事务rollback only,如果你想不回滚,try catch得在C类中,而不是B类. C有多个调用者,逻辑不一样,可以在C里再加一个方法try catch原方法,B调用新方法.
dkwuxiang 2020-04-22
  • 打赏
  • 举报
回复
比如 贴一下,transaction manager 的配置,以及事务切面的配置
不忍直视 2020-04-22
  • 打赏
  • 举报
回复
引用 14 楼 dkwuxiang的回复:
没有加注解就是没有声明式事务? 应用没有配置 transaction-manager 吗?所有方法事务都是通过自己 加注解起事务的吗?
额,transaction manager肯定有配,原来是问这个,我还以为在问c上面有没有加呢,不过有一个就是D(就是c里面调的那个查询方法)是有一个只读事务的,这个是因为公司的框架,为每个service的父类都加了个transactional注解,但是C是没有继承这个service基类的,D有继承,所以D里面应该有一个只读事务
dkwuxiang 2020-04-22
  • 打赏
  • 举报
回复
有一种原因是:声明式事务设置的是 PROPAGATION_REQUIRED 级别,c的事务加入到b中,c出现运行时异常,抛出异常时transaction被设置为rollback-only了, 但在b中异常又被处理了, b 执行完后,transaction会执commit操作,但是transaction已经被设置为rollback-only了。

	@Override
	public final void commit(TransactionStatus status) throws TransactionException {
		if (status.isCompleted()) {
			throw new IllegalTransactionStateException(
					"Transaction is already completed - do not call commit or rollback more than once per transaction");
		}

		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		if (defStatus.isLocalRollbackOnly()) {
			if (defStatus.isDebug()) {
				logger.debug("Transactional code has requested rollback");
			}
			processRollback(defStatus);
			return;
		}
		if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
			if (defStatus.isDebug()) {
				logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
			}
			processRollback(defStatus);
			// Throw UnexpectedRollbackException only at outermost transaction boundary
			// or if explicitly asked to.
			if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
				throw new UnexpectedRollbackException(
						"Transaction rolled back because it has been marked as rollback-only");
			}
			return;
		}

		processCommit(defStatus);
	}

dkwuxiang 2020-04-22
  • 打赏
  • 举报
回复
没有加注解就是没有声明式事务? 应用没有配置 transaction-manager 吗?所有方法事务都是通过自己 加注解起事务的吗?
不忍直视 2020-04-22
  • 打赏
  • 举报
回复
引用 12 楼 maradona1984的回复:
[quote=引用 11 楼 不忍直视 的回复:] 但是我在C上面并没有加Trasanctional注解,讲道理,C出现异常之后不应该将B的事务状态设置为回滚吧
有做声明式事务?[/quote] C没有加声明式事务,也就是没有加Transactional注解
不忍直视 2020-04-21
  • 打赏
  • 举报
回复
大神们,帮忙看看呀 困扰我好久了

67,513

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧