springboot ApplicationEvent调用(或者@Async方法调用)导致 JPA 的save方法未执行
问题发现
之前某个功能一直正常的,直到一天突然发现,有个操作未正常生成数据。通过debug发现,问题关联功能执行的save方法未执行成功,且未出现异常,save方法执行了,只是数据未保存。
同时发现,此方法还有一个controller方法入口,通过测试,发现此方法调用是正常的,数据可以正常保存。
但出现问题的地方,是从ApplicationEvent事件进来的,猜测是因为异步方法(新的子线程)造成的。因此模拟@Async异步调用,也能复现这个bug。
寻找解决
通过debug源码一直未能找到问题原因。
网上搜索,发现有类似解决:用JpaTransactionManager替换DataSourceTransactionManager作为系统的事务管理器。
问题验证
通过debug发现当前事务管理器确实是DataSourceTransactionManager,按理说应该是默认的JpaTransactionManager才对,因为配置springboot的JPA时,会默认注入JpaTransactionManager的bean。(详见:JpaBaseConfiguration类)
问题解决
全局搜索DataSourceTransactionManager时,发现近期有手动注入DataSourceTransactionManager的bean对象,原来是引入了mybatis组件,就同时定义了事务管理器。可是DataSourceTransactionManager与JpaTransactionManager都是TransactionManager的实现类,所以JpaTransactionManager就不会生成(JpaBaseConfiguration类中:@ConditionalOnMissingBean({TransactionManager.class})),所以DataSourceTransactionManager成为了系统的默认事务管理器。
解决:从配置类中,将DataSourceTransactionManager的Bean去掉即可
思考
为什么DataSourceTransactionManager可以支持同步调用(单线程)的JPA,而异步调用的就不行?
而JpaTransactionManager则可以?
通过读JpaTransactionManager源码时,发现doBegin(事务开始)有一段值得注意的代码
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
JpaTransactionObject txObject = (JpaTransactionObject) transaction;
if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
throw new IllegalTransactionStateException(
"Pre-bound JDBC Connection found! JpaTransactionManager does not support " +
"running within DataSourceTransactionManager if told to manage the DataSource itself. " +
"It is recommended to use a single JpaTransactionManager for all transactions " +
"on a single DataSource, no matter whether JPA or JDBC access.");
}
try {
// 事务对象的entityManagerHolder是否为空?且 资源是否与事务同步?
if (!txObject.hasEntityManagerHolder() ||
txObject.getEntityManagerHolder().isSynchronizedWithTransaction()) {
// 则创建新的EntityManager
EntityManager newEm = createEntityManagerForTransaction();
if (logger.isDebugEnabled()) {
logger.debug("Opened new EntityManager [" + newEm + "] for JPA transaction");
}
// 设置EntityManagerHolder
txObject.setEntityManagerHolder(new EntityManagerHolder(newEm), true);
}
...省略后续代码
所以我认为是,DataSourceTransactionManager没有针对异步调用时,对事务的处理。而JpaTransactionManager有
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下