#java #postgresql #spring-boot #hibernate #spring-data
Вопрос:
У меня ошибка с обработкой транзакций в весенних данных.
Версия Spring boot с коррелирующими данными spring — 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).
Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.someIncrement] after exception: javax.persistence.TransactionRequiredException: Executing an update/delete query
Я понятия не имею, почему это происходит, потому что я вижу журналы, в которых начата транзакция, а затем завершение транзакции происходит с ошибкой. Я пытался найти похожие ошибки на StackOverflow или подобных страницах, однако ни одна из них не работает.
Знаете ли вы, почему возникает эта ошибка? И как это исправить?
PS: Мой транзакционный метод взят из: import org.springframework.transaction.аннотация.Транзакционные;
Трассировка стека:
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. Я убедился, что запуск класса, вызывающего этот метод, работает так, как ожидалось. Проблема начинается с класса before ->, который отправляет событие, и обработчик событий(с аннотацией @TransactionalEventListener) получает его после фиксации, а затем вызывается тот же путь и возникает исключение.
Поэтому в настоящее время для меня похоже, что @TransactionalEventListener плохо работает с @Transactional.
ОБНОВЛЕНИЕ 2:
Это работает только тогда, когда настроена @Transaction(распространение = REQUIRES_NEW) (как указано в комментариях. Однако я хотел бы понять, почему это так. Обычная @Транзакция работает так же, как @Транзакция(распространение = REQUIRES_NEW), когда транзакция не выполняется, и это непосредственно этот случай. Я до сих пор не могу сказать, почему нам нужно, чтобы распространение требовало новых, а не простых транзакций.
Комментарии:
1. Можете ли вы прикрепить трассировку стека
2. Я прикрепил дорожку стека
Ответ №1:
Я думаю, что вам нужно создать новую транзакцию ( @Transactional(propagation = REQUIRES_NEW)
при вызове таких служб из прослушивателя транзакций.
Комментарии:
1. Да, я проверил, что после @ TransactionalEventListener обычный @ Transactional не работает, но нам нужен @ Transactional(распространение = REQUIRES_NEW). Но почему это так? обычный @ Транзакционный работает так же, как и при распространении, требует создания, если транзакция не выполняется. И после получения события транзакция не выполняется
2. Вам придется спросить у исполнителя менеджера транзакций (не уверен, что вы используете). Я думаю, проблема в том, что ваш поток, в котором выполняется прослушиватель, все еще связан с транзакцией, поэтому запуск другого транзакционного метода будет участвовать в этой транзакции, даже если она уже завершена.
3. Но согласно весенним документам событие отправляется только тогда, когда транзакция зафиксирована. Даже когда транзакция зафиксирована, означает ли это, что поток все еще связан с транзакцией?
4. Я почти уверен, что это так, иначе это потеряло бы контекст транзакции, но, как я уже сказал, вы должны спросить исполнителя менеджера транзакций.