Spring数据休眠upsert事务错误@TransactionalEventListener和@Transactinal
Posted
技术标签:
【中文标题】Spring数据休眠upsert事务错误@TransactionalEventListener和@Transactinal【英文标题】:Spring data hibernate upsert transaction error @TransactionalEventListener and @Transactinal 【发布时间】:2021-06-19 03:25:39 【问题描述】:我在 Spring Data 中处理事务时出错。
具有相关弹簧数据的 Spring Boot 版本 - 2.3.7。 Java - 11
我的服务类中有一个方法(注解为服务)
@Transactional
public void incrementSomething(SomeData data)
myRepository.someIncrement(data);
我的存储库(我想进行 upsert 操作):
@Repository
public interface MyRepository extends PagingAndSortingRepository<SomeData, Long>
@Transactional
@Modifying
@Query(value = "INSERT INTO my_table (.....) VALUES (.....) " +
"ON CONFLICT ON CONSTRAINT some_contraint DO UPDATE " +
"set something = my_table.something + 1", nativeQuery = true)
public void someIncrement(@Param("someparam") String data);
但是我发生了一个奇怪的异常(我在 springframework 中启用了 TRACE 事务)。
Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.someIncrement] after exception: javax.persistence.TransactionRequiredException: Executing an update/delete query
我不知道为什么会这样,因为我可以看到事务开始的日志,然后完成事务发生错误。我试图在 *** 或类似页面上寻找类似的错误,但是它们都不起作用。
你知道为什么会出现这个错误吗?以及如何解决?
PS: 我的交易方法来自: 导入 org.springframework.transaction.annotation.Transactional;
堆栈跟踪:
org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:403)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:257)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:531)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:154)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:149)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy222.incrementCounter(Unknown Source)
...
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
...
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.context.event.ApplicationListenerMethodAdapter.doInvoke(ApplicationListenerMethodAdapter.java:305)
at org.springframework.context.event.ApplicationListenerMethodAdapter.processEvent(ApplicationListenerMethodAdapter.java:190)
at org.springframework.transaction.event.ApplicationListenerMethodTransactionalAdapter$TransactionSynchronizationEventAdapter.processEvent(ApplicationListenerMethodTransactionalAdapter.java:129)
at org.springframework.transaction.event.ApplicationListenerMethodTransactionalAdapter$TransactionSynchronizationEventAdapter.afterCompletion(ApplicationListenerMethodTransactionalAdapter.java:118)
at org.springframework.transaction.support.TransactionSynchronizationUtils.invokeAfterCompletion(TransactionSynchronizationUtils.java:171)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.invokeAfterCompletion(AbstractPlatformTransactionManager.java:989)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerAfterCompletion(AbstractPlatformTransactionManager.java:964)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:785)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:633)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:386)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
...
at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
...
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:413)
at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1668)
at org.springframework.data.jpa.repository.query.JpaQueryExecution$ModifyingExecution.doExecute(JpaQueryExecution.java:238)
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:88)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:154)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:142)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor$QueryMethodInvoker.invoke(QueryExecutorMethodInterceptor.java:195)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:152)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:130)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
... 56 common frames omitted
更新 1: 我已经使用测试容器和相同的 postgres 版本添加了测试。我已经验证运行调用此方法的类可以按预期工作。问题从之前的类开始 -> 它发送一个事件,事件处理程序(注释为@TransactionalEventListener)在提交后接收它,然后调用相同的路径并发生异常。
所以目前对我来说,@TransactionalEventListener 似乎不适用于 @Transactional。
更新 2:
它仅在设置 @Transaction(propagation = REQUIRES_NEW) 时才有效(如 cmets 中所述。但是我想了解为什么会这样。正常@Transaction 与 @Transaction(propagation = REQUIRES_NEW) 相同时没有事务正在运行,直接就是这种情况。我仍然无法说明为什么我们需要传播需要新的而不是简单的事务。
【问题讨论】:
你能附上堆栈跟踪吗 我已附加堆栈跟踪 【参考方案1】:我认为您需要创建一个新事务(@Transactional(propagation = REQUIRES_NEW)
从事务侦听器中调用此类服务时。
【讨论】:
是的,我检查过@TransactionalEventListener 正常后@Transactional 不起作用,但我们需要@Transactional(propagation = REQUIRES_NEW)。但这是为什么呢? normal @Transactional 的工作方式与传播需要新的相同,如果没有事务正在运行。并且在收到事件后没有事务在运行 您将不得不询问事务管理器的实现者(不确定您使用的是什么)。我认为问题在于您运行侦听器的线程仍然与事务相关联,因此运行另一个事务方法将参与该事务,即使它已经完成。 但根据spring docs事件仅在事务提交时发送。即使提交了事务,是否意味着线程仍然与事务相关联? 我很确定是这样,否则会丢失事务上下文,但是就像我说的,你得问事务管理器的实现者。以上是关于Spring数据休眠upsert事务错误@TransactionalEventListener和@Transactinal的主要内容,如果未能解决你的问题,请参考以下文章
sqlserver中事务总结:begin tran,rollback tran,commit tran