У меня возникли проблемы с совершением транзакции в моем методе @Transactional:
methodA() {
methodB()
}
@Transactional
methodB() {
...
em.persist();
...
em.flush();
log("OK");
}
Когда я вызываю methodB () из methodA (), метод проходит успешно, и я вижу «ОК» в моих журналах. Но потом я получаю
Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at methodA()...
- Контекст methodB полностью отсутствует в исключении - я полагаю, что это нормально?
- Что-то внутри методаB () пометило транзакцию только как откат? Как я могу это узнать? Есть ли, например, способ проверить что-то вроде
getCurrentTransaction().isRollbackOnly()?
этого - я мог бы пройти через метод и найти причину.
Ответы:
Когда вы помечаете свой метод как
@Transactional
, возникновение любого исключения внутри вашего метода будет отмечать окружающий TX только как откат (даже если вы их поймаете). Вы можете использовать другие атрибуты@Transactional
аннотации, чтобы предотвратить откат, например:@Transactional(rollbackFor=MyException.class, noRollbackFor=MyException2.class)
источник
noRollbackFor=Exception.class
, но вроде не действует - для унаследованных исключений работает?methodC
в своем первом посте). ОбаmethodB
иmethodC
использовать тот же TX и всегда наиболее специфичные@Transactional
используются аннотации, поэтому , когдаmethodC
бросает исключение, окружая TX будет отмечен как откат только. Вы также можете использовать разные маркеры распространения, чтобы предотвратить это.EmptyResultDataAccessException
исключение для транзакции только для чтения, и я получил ту же ошибку. Изменение моей аннотации для@Transactional(readOnly = true, noRollbackFor = EmptyResultDataAccessException.class)
устранения проблемы.@Transactional
оболочку прокси, то есть не перехвачены . Смотрите другой ответ Войтеха, чтобы узнать больше. Могут существовать вложенные@Transactional
методы, которые могут пометить только откат вашей транзакции.noRollbackFor
работает только еслиglobalRollbackOnParticipationFailure=false
Я наконец понял проблему:
methodA() { methodB() } @Transactional(noRollbackFor = Exception.class) methodB() { ... try { methodC() } catch (...) {...} log("OK"); } @Transactional methodC() { throw new ...(); }
Что происходит, так это то, что даже если у объекта
methodB
есть правильная аннотация, уmethodC
него нет. Когда генерируется исключение, вторая@Transactional
в любом случае отмечает только первую транзакцию как откат.источник
propagation=requires_new
то methodB не откатится ?methodC
должен находиться в другом компоненте / службе Spring или каким-либо образом доступен через прокси-сервер Spring. В противном случае Spring не сможет узнать о вашем исключении. Только исключение, которое проходит через@Transactional
аннотацию, может пометить транзакцию как откатную.Чтобы быстро получить вызывающее исключение без необходимости перекодировать или перестраивать , установите точку останова на
org.hibernate.ejb.TransactionImpl.setRollbackOnly() // Hibernate < 4.3, or org.hibernate.jpa.internal.TransactionImpl() // as of Hibernate 4.3
и поднимаются в стеке, обычно к какому-нибудь перехватчику. Там вы можете прочитать вызывающее исключение из некоторого блока catch.
источник
org.hibernate.jpa.internal.TransactionImpl
org.hibernate.engine.transaction.internal.TransactionImpl
и метод естьsetRollbackOnly
.Я боролся с этим исключением при запуске своего приложения.
Наконец, проблема была в запросе sql . я имею в виду, что запрос неверен.
пожалуйста, подтвердите свой запрос. Это мое предложение
источник
Ищите исключения, которые выбрасываются и попадают в
...
разделах вашего кода. Исключения времени выполнения и отката приложений вызывают откат, когда они выбрасываются из бизнес-метода, даже если они обнаружены в другом месте.Вы можете использовать контекст, чтобы узнать, помечена ли транзакция для отката.
@Resource private SessionContext context; context.getRollbackOnly();
источник
SessionContext
стандартный класс весной? Мне кажется, что это скорее EJB3, и его нет в моем приложении Spring.TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()
доступного.Нашел хорошее объяснение с решениями: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/
1) удалите @Transacional из вложенного метода, если он действительно не требует управления транзакциями. Таким образом, даже если у него есть исключение, он просто пузырится и не влияет на транзакционные материалы.
ИЛИ:
2) если вложенному методу требуется управление транзакцией, сделайте его REQUIRE_NEW для политики распространения таким образом, даже если выдает исключение и помечено только как откат, вызывающий не будет затронут.
источник
отключите диспетчер транзакций в вашем Bean.xml
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
закомментируйте эти строки, и вы увидите исключение, вызывающее откат;)
источник
примените приведенный ниже код в productRepository
@Query("update Product set prodName=:name where prodId=:id ") @Transactional @Modifying int updateMyData(@Param("name")String name, @Param("id") Integer id);
в то время как в тесте junit применяется код ниже
@Test public void updateData() { int i=productRepository.updateMyData("Iphone",102); System.out.println("successfully updated ... "); assertTrue(i!=0); }
он отлично работает для моего кода
источник