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

单个休眠会话中的多个事务(使用 Spring)

BEGIN TRAN...COMMIT TRAN 意思与用法

带有 2 个数据库的休眠随机“会话已关闭错误”

spring事务失效的几种场景以及原因

为啥休眠 5.3 不支持带有 infinispan 的事务缓存