当前位置:网站首页>嵌套事务 UnexpectedRollbackException 分析与事务传播策略
嵌套事务 UnexpectedRollbackException 分析与事务传播策略
2022-07-25 11:22:00 【JackLi0812】
UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
1. 问题描述与重现
jfoa提供了Audit的功能, 目标方法(比如添加用户)存在事务管理,
在 Audit 中需要去查询用户(事务管理), 最后在目标方法执行后执行 Audit 记录的插入(事务管理).
即添加用户(事务)中查询用户(事务)就会抛出异常:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
字面意思是:
出现了不可预知的回滚异常,因为事务已经被标志位只能回滚,所以事务回滚了.下面来重现和描述一下这个问题.
@Around("audit()")
public Object recordLog(ProceedingJoinPoint pjp) throws Throwable {
Log log = null;
Customer principal = null;
try {
// note1: 这里获取当前登录的用户, 注意这个方法可以抛出异常
// 1) getCurrentCustomer 在事务管理中
// 2) catch 了方法调用并没有继续抛出异常
principal = customerService.getCurrentCustomer();
} catch (Exception ignore) {
LOGGER.debug("Get principal error!", ignore);
}
// ... 省略一些中间代码
Object result;
try {
// note3: 具体的目标方法
result = pjp.proceed();
}
catch(Throwable throwable) {
if(log != null) {
log.setMessage("Execute Failed: " + throwable.getMessage());
}
throw throwable;
}
finally {
// ...
}
return result;
}
// note2: 目标方法有事务管理
@Audit(ResourceEnum.Customer)
@Transactional
@Override
public Integer insertCustomer(@AuditObject("getName()") Customer user) {
if(isValid(user)) {
user.setPassword(
SecurityUtil.generatorPassword(user.getAccount(), user.getPassword()));
return customerDao.insert(user);
}
return null;
}
注意上方的
note1和note2, 在这种情况下就会出现本文的异常.
2. 原理剖析
因为 spring 的
@Transactional注解默认的事务传播策略为Propagation.REQUIRED,
PROPAGATION_REQUIRED的意思是,当前有事务,则使用当前事务,当前无事务则创建事务。因此, 当
目标方法被调用时, 由于目标方法存在事务, 当目标方法被调用时会开启事务,
当执行到note1时,getCurrentCustomer方法也
存在事务, 根据事务传播策略, spring 就会使用目标方法的事务,
但是getCurrentCustomer有 try-catch 处理, 并且catch没有继续抛出事务,
因此当getCurrentCustomer抛出异常时, spring 要执行 rollback 操作, 但是 catch
没有继续抛出异常, 还会继续调用到note3处继续执行, 当目标方法被调用完成后
由于是正常调用完毕, 因此又需要执行 commit, 所以就会引发该异常.
2.0 事务传播策略简介
spring 定义了如下传播策略:
public enum Propagation {
/** * 当前有事务,则使用当前事务,当前无事务则创建事务 */
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
/** * 支持当前事务,如果不存在,以非事务方式执行。 * 注:对于事务同步的事务管理器,与根本没有事务略有不同, * 因为它定义了同步将应用的事务范围。 * 结果,相同的资源(JDBC连接、Hibernate会话等) * 将为整个指定范围共享。注意,这取决于 * 事务管理器的实际同步配置。 */
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
/** * 如果当前与事务则使用当前事务, 如果当前没事务则抛出异常. */
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
/** * 无论当前是否有事务都创建一个新的事务. * 如果当前有事务则挂起当前事务. */
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
/** * 以非事务方式运行, 如果当前有事务, 则挂起当前事务 */
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
/** * 以非事务方式运行, 如果当前有事务, 则抛出异常 */
NEVER(TransactionDefinition.PROPAGATION_NEVER),
/** * 如果当前事务存在,则在嵌套事务中执行,否则行为类似{@code REQUIRED}. * 注意: 嵌套事务的实际创建只适用于特定的事务管理器。 * 这只适用于JDBC DataSourceTransactionManager。 * 一些JTA提供程序可能也支持嵌套事务。 */
NESTED(TransactionDefinition.PROPAGATION_NESTED);
2.1 默认传播策略
spring 的
@Transactional注解默认的事务传播策略为Propagation.REQUIRED
public @interface Transactional {
// ...
/** * The transaction propagation type. * <p>Defaults to {@link Propagation#REQUIRED}. * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior() */
Propagation propagation() default Propagation.REQUIRED;
2.2 问题总结
当 methodA 调用 methodB, 并且两个方都有事务(事务嵌套),
且传播策略都为Propagation.REQUIRED时, 就会导致两个方法
共用一个事务,methodB将事务标志为回滚,methodA中commit这个事务,
然后就会出现事务已经被标志回滚(methodB标志的)的异常信息.
3. 解决方法
知道了问题出现的原因, 自然就知道了解决办法, 但是不同的业务场景解决方式可能不同,
大家需要结合自己的业务场景选择合适的解决方案
3.1 修改 try-catch
对内部方法 methodB 不要进行 try-catch 或者 catch 继续抛出异常.
3.2 修改事务传播策略
- 如果修改外部事务 methodA 的事务传播策略, 则 methodA 只能取消事务(一般不可用).
- 修改内部 methodB 的事务传播策略, 一般可改为
REQUIRES_NEW,NOT_SUPPORTED,NESTED.NOT_SUPPORTED都把内部事务转换为非事务运行.REQUIRES_NEW会创建一个新事务运行.NESTED在嵌套事务运行.
比如帅帅我这里的业务场景, 首先是需要事务管理, 因此我这里可以使用
REQUIRES_NEW,NESTED,
其次, audit 记录以及,getCurrentCustomer无论何时都不应该影响核心业务逻辑的运行,因此,
最终我选择修改事务传播策略为REQUIRES_NEW来解决这个问题. 如果您的嵌套事务和主事务有关联关系,
那么您就应该选择NESTED.
最后, 还需要注意一点: 同一个类中,事务嵌套以最外层的方法为准,嵌套的事务失效;不同类中嵌套的事务才会生效;

边栏推荐
- 【Debias】Model-Agnostic Counterfactual Reasoning for Eliminating Popularity Bias in RS(KDD‘21)
- Solved files' name is invalid or doors not exist (1205)
- 图神经网络用于推荐系统问题(IMP-GCN,LR-GCN)
- R语言ggplot2可视化:可视化散点图并为散点图中的部分数据点添加文本标签、使用ggrepel包的geom_text_repel函数避免数据点之间的标签互相重叠(为数据点标签添加线段、指定线段的角度
- 硬件连接服务器 tcp通讯协议 gateway
- Introduction to pl/sql, very detailed notes
- R语言ggplot2可视化:使用ggpubr包的ggviolin函数可视化小提琴图、设置add参数在小提琴内部添加抖动数据点以及均值标准差竖线(jitter and mean_sd)
- php 一台服务器传图片到另一台上 curl post file_get_contents保存图片
- 【黑马早报】运营23年,易趣网宣布关停;蔚来对大众CEO抛出橄榄枝;华为天才少年曾放弃360万年薪;尹烨回应饶毅炮轰其伪科学...
- Brpc source code analysis (I) -- the main process of RPC service addition and server startup
猜你喜欢

NLP knowledge - pytorch, back propagation, some small pieces of notes for predictive tasks

Brpc source code analysis (VIII) -- detailed explanation of the basic class eventdispatcher

Transformer variants (routing transformer, linformer, big bird)

Differences in usage between tostring() and new string()

【CTR】《Towards Universal Sequence Representation Learning for Recommender Systems》 (KDD‘22)

brpc源码解析(一)—— rpc服务添加以及服务器启动主要过程

利用wireshark对TCP抓包分析

Zero-Shot Image Retrieval(零样本跨模态检索)

【GCN-RS】Towards Representation Alignment and Uniformity in Collaborative Filtering (KDD‘22)

Zuul网关使用
随机推荐
【高并发】高并发场景下一种比读写锁更快的锁,看完我彻底折服了!!(建议收藏)
R语言使用lm函数构建多元回归模型(Multiple Linear Regression)、使用step函数构建前向逐步回归模型筛选预测变量的最佳子集、scope参数指定候选预测变量
[RS sampling] a gain tuning dynamic negative sampler for recommendation (WWW 2022)
【多模态】《TransRec: Learning Transferable Recommendation from Mixture-of-Modality Feedback》 Arxiv‘22
【GCN多模态RS】《Pre-training Representations of Multi-modal Multi-query E-commerce Search》 KDD 2022
【GCN-RS】MCL: Mixed-Centric Loss for Collaborative Filtering (WWW‘22)
[multimodal] hit: hierarchical transformer with momentum contract for video text retrieval iccv 2021
Differences in usage between tostring() and new string()
【高并发】我用10张图总结出了这份并发编程最佳学习路线!!(建议收藏)
Zero-Shot Image Retrieval(零样本跨模态检索)
PL/SQL入门,非常详细的笔记
Brpc source code analysis (V) -- detailed explanation of basic resource pool
【GCN-RS】Are Graph Augmentations Necessary? Simple Graph Contrastive Learning for RS (SIGIR‘22)
【高并发】SimpleDateFormat类到底为啥不是线程安全的?(附六种解决方案,建议收藏)
通过Referer请求头实现防盗链
[untitled]
NLP知识----pytorch,反向传播,预测型任务的一些小碎块笔记
基于TCP/IP在同一局域网下的数据传输
[imx6ull notes] - a preliminary exploration of the underlying driver of the kernel
【AI4Code】《GraphCodeBERT: Pre-Training Code Representations With DataFlow》 ICLR 2021