Mam problemy z zatwierdzeniem transakcji w ramach mojej metody @Transactional:
methodA() {
methodB()
}
@Transactional
methodB() {
...
em.persist();
...
em.flush();
log("OK");
}
Kiedy wywołuję metodę MethodB () z metody MethodA (), metoda kończy się pomyślnie i w moich dziennikach jest wyświetlany komunikat „OK”. Ale wtedy rozumiem
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()...
- W wyjątku całkowicie brakuje kontekstu methodB - co, jak sądzę, jest w porządku?
- Coś w metodzie MethodB () oznaczyło transakcję jako tylko wycofanie? Jak mogę się tego dowiedzieć? Czy jest na przykład sposób, aby sprawdzić coś takiego
getCurrentTransaction().isRollbackOnly()?
- w ten sposób mógłbym przejść przez metodę i znaleźć przyczynę.
Odpowiedzi:
Kiedy oznaczysz swoją metodę jako
@Transactional
, wystąpienie jakiegokolwiek wyjątku w metodzie spowoduje oznaczenie otaczającego TX jako tylko wycofania (nawet jeśli je złapiesz). Możesz użyć innych atrybutów@Transactional
adnotacji, aby zapobiec jej wycofywaniu, na przykład:@Transactional(rollbackFor=MyException.class, noRollbackFor=MyException2.class)
źródło
noRollbackFor=Exception.class
, ale wydaje się, że nie ma to żadnego efektu - czy działa w przypadku odziedziczonych wyjątków?methodC
w swoim pierwszym poście). ObamethodB
imethodC
używają tego samego TX i zawsze@Transactional
używana jest najbardziej szczegółowa adnotacja, więc gdymethodC
zgłasza wyjątek, otaczający TX zostanie oznaczony jako tylko wycofanie. Aby temu zapobiec, możesz również użyć różnych markerów propagacji.EmptyResultDataAccessException
wyjątek w transakcji tylko do odczytu i otrzymałem ten sam błąd. Zmieniam adnotację, aby@Transactional(readOnly = true, noRollbackFor = EmptyResultDataAccessException.class)
rozwiązać problem.@Transactional
opakowanie proxy, czyli nie są przechwytywane . Zobacz inną odpowiedź od Vojtěcha, aby poznać całą historię. Mogą istnieć zagnieżdżone@Transactional
metody, które mogą oznaczać tylko wycofanie transakcji.noRollbackFor
działa tylko wtedy, gdyglobalRollbackOnParticipationFailure=false
W końcu zrozumiałem problem:
methodA() { methodB() } @Transactional(noRollbackFor = Exception.class) methodB() { ... try { methodC() } catch (...) {...} log("OK"); } @Transactional methodC() { throw new ...(); }
Dzieje się tak, że nawet jeśli
methodB
ma właściwą adnotację,methodC
nie. Gdy wyjątek zostanie zgłoszony, druga@Transactional
transakcja i tak oznacza pierwszą transakcję jako tylko wycofanie.źródło
propagation=requires_new
to methodB nie wycofa się?methodC
musi znajdować się w innym elemencie / usłudze Spring lub w jakiś sposób uzyskać do niego dostęp za pośrednictwem serwera proxy Spring. W przeciwnym razie Spring nie będzie mieć możliwości dowiedzenia się o Twoim wyjątku. Jedyny wyjątek, który przechodzi przez@Transactional
adnotację, może oznaczać transakcję jako tylko do wycofania.Aby szybko pobrać powodujący wyjątek bez konieczności ponownego kodowania lub przebudowywania , ustaw punkt przerwania na
org.hibernate.ejb.TransactionImpl.setRollbackOnly() // Hibernate < 4.3, or org.hibernate.jpa.internal.TransactionImpl() // as of Hibernate 4.3
i wejdź na stos, zwykle do jakiegoś Interceptora. Tam możesz przeczytać powodujący wyjątek z jakiegoś bloku catch.
źródło
org.hibernate.jpa.internal.TransactionImpl
org.hibernate.engine.transaction.internal.TransactionImpl
a metoda tosetRollbackOnly
.Miałem problem z tym wyjątkiem podczas uruchamiania aplikacji.
Wreszcie problem dotyczył kwerendy sql . mam na myśli, że zapytanie jest błędne.
proszę zweryfikuj swoje zapytanie. To jest moja sugestia
źródło
Poszukaj wyjątków rzucanych i przechwytywanych w
...
sekcjach kodu. Wyjątki aplikacji w czasie wykonywania i wycofywania zmian powodują wycofanie, gdy zostaną wyrzucone z metody biznesowej, nawet jeśli zostaną przechwycone w innym miejscu.Możesz użyć kontekstu, aby dowiedzieć się, czy transakcja jest oznaczona do wycofania.
@Resource private SessionContext context; context.getRollbackOnly();
źródło
SessionContext
na wiosnę są standardowe zajęcia? Wydaje mi się, że jest to raczej EJB3 i nie ma go w mojej aplikacji wiosennej.TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()
dostępne.Znalazłem dobre wyjaśnienie dzięki rozwiązaniom: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/
1) usuń @Transacional z metody zagnieżdżonej, jeśli tak naprawdę nie wymaga kontroli transakcji. Więc nawet on ma wyjątek, po prostu bulgocze i nie wpływa na transakcje.
LUB:
2) jeśli metoda zagnieżdżona wymaga kontroli transakcji, ustaw ją jako REQUIRE_NEW dla zasad propagacji w ten sposób, nawet jeśli zgłosi wyjątek i zostanie oznaczona jako tylko wycofywanie, nie wpłynie to na obiekt wywołujący.
źródło
wyłącz menedżera transakcji w pliku 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>
zakomentuj te linie, a zobaczysz wyjątek powodujący wycofanie;)
źródło
zastosuj poniższy kod w productRepository
@Query("update Product set prodName=:name where prodId=:id ") @Transactional @Modifying int updateMyData(@Param("name")String name, @Param("id") Integer id);
podczas gdy w teście junit zastosuj poniższy kod
@Test public void updateData() { int i=productRepository.updateMyData("Iphone",102); System.out.println("successfully updated ... "); assertTrue(i!=0); }
działa dobrze dla mojego kodu
źródło