Nur als Rollback gekennzeichnete Transaktion: Wie finde ich die Ursache?

91

Ich habe Probleme beim Festschreiben einer Transaktion innerhalb meiner @ Transactional-Methode:

methodA() {
    methodB()
}

@Transactional
methodB() {
    ...
    em.persist();
    ...
    em.flush();
    log("OK");
}

Wenn ich methodB () von methodA () aus aufrufe, ist die Methode erfolgreich und ich kann "OK" in meinen Protokollen sehen. Aber dann verstehe ich

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()...
  1. Der Kontext von Methode B fehlt in der Ausnahme vollständig - was ist in Ordnung, nehme ich an?
  2. Etwas in der Methode B () hat die Transaktion nur als Rollback markiert? Wie kann ich das herausfinden? Gibt es zum Beispiel eine Möglichkeit, so etwas zu überprüfen getCurrentTransaction().isRollbackOnly()?- so könnte ich die Methode durchgehen und die Ursache finden.
Vojtěch
quelle
verwandt: stackoverflow.com/q/25322658/697313
Yaroslav Stavnichiy
Interessant ist, dass dieser Fehler manchmal auch angezeigt wird, wenn Ihre Datenbanktabelle nicht vorhanden ist.
Ng Sek Long

Antworten:

101

Wenn Sie Ihre Methode als markieren, markiert das @TransactionalAuftreten einer Ausnahme innerhalb Ihrer Methode den umgebenden TX nur als Rollback (selbst wenn Sie sie abfangen). Sie können andere @TransactionalAnnotationsattribute verwenden, um ein Zurücksetzen zu verhindern, wie z.

@Transactional(rollbackFor=MyException.class, noRollbackFor=MyException2.class)
Ean V.
quelle
6
Nun, ich habe versucht zu verwenden noRollbackFor=Exception.class, aber es scheint keine Wirkung zu haben - funktioniert es für geerbte Ausnahmen?
Vojtěch
6
Ja tut es. Wenn Sie sich Ihre eigene Antwort ansehen, ist das richtig (Sie haben sie methodCin Ihrem ersten Beitrag nicht angegeben). Beide methodBund methodCverwenden denselben TX, und es wird immer die spezifischste @TransactionalAnmerkung verwendet. Wenn also methodCdie Ausnahme ausgelöst wird, wird der umgebende TX als nur Rollback markiert. Sie können auch verschiedene Ausbreitungsmarker verwenden, um dies zu verhindern.
Ean V
@Eine Ausnahme innerhalb Ihrer Methode markiert den umgebenden TX nur als Rollback. Gilt dies auch für readOnly-Transaktionen?
Marko Vranjkovic
1
@lolotron @Ean Ich kann bestätigen, dass dies tatsächlich für eine schreibgeschützte Transaktion gilt. Meine Methode hat eine EmptyResultDataAccessExceptionAusnahme für eine schreibgeschützte Transaktion ausgelöst, und ich habe den gleichen Fehler erhalten. Ändern meiner Anmerkung, um @Transactional(readOnly = true, noRollbackFor = EmptyResultDataAccessException.class)das Problem zu beheben.
cbmeeks
5
Diese Antwort ist falsch. Spring kennt nur Ausnahmen, die den @TransactionalProxy-Wrapper durchlaufen , dh nicht erfasst werden . Siehe die andere Antwort von Vojtěch für die ganze Geschichte. Es kann verschachtelte @TransactionalMethoden geben, die nur das Transaktions-Rollback markieren können.
Jaroslaw Stavnichiy
67

Ich habe das Problem endlich verstanden:

methodA() {
    methodB()
}

@Transactional(noRollbackFor = Exception.class)
methodB() {
    ...
    try {
        methodC()
    } catch (...) {...}
    log("OK");
}

@Transactional
methodC() {
    throw new ...();
}

Was passiert ist, dass, obwohl das methodBdie richtige Anmerkung hat, das methodCnicht. Wenn die Ausnahme ausgelöst wird, @Transactionalmarkiert die zweite die erste Transaktion ohnehin nur als Rollback.

Vojtěch
quelle
5
Der Status der Transaktion wird in einer lokalen Thread-Variablen gespeichert. Wenn die Feder methodC abfängt und das Flag als Rollback setzt, ist Ihre Transaktion bereits für das Rollback markiert. Jede weitere Unterdrückung der Ausnahme hilft nicht weiter, da Sie beim endgültigen
lebt am
@ Vojtěch Jede Art und Weise hypothetisch , wenn methodC haben propagation=requires_newdann methodeB wird nicht Rollback?
DeFreitas
4
methodCmuss sich in einem anderen Spring Bean / Service befinden oder irgendwie über den Spring Proxy aufgerufen werden. Andernfalls hat Spring keine Möglichkeit, von Ihrer Ausnahme zu erfahren. Nur eine Ausnahme, die eine @TransactionalAnmerkung durchläuft, kann die Transaktion als nur Rollback kennzeichnen.
Jaroslaw Stavnichiy
43

Setzen Sie einen Haltepunkt auf, um die verursachende Ausnahme schnell abzurufen, ohne sie neu codieren oder neu erstellen zu müssen

org.hibernate.ejb.TransactionImpl.setRollbackOnly() // Hibernate < 4.3, or
org.hibernate.jpa.internal.TransactionImpl() // as of Hibernate 4.3

und gehe in den Stapel, normalerweise zu einem Interceptor. Dort können Sie die verursachende Ausnahme aus einem catch-Block lesen.

FelixJongleur42
quelle
6
In Hibernate 4.3.11 ist esorg.hibernate.jpa.internal.TransactionImpl
Wim Deblauwe
Sehr gut, mein Freund!
Rafael Andrade
Vielen Dank! In neueren Versionen von Hibernate (5.4.17) ist die Klasse org.hibernate.engine.transaction.internal.TransactionImplund die Methode ist setRollbackOnly.
Peter Catalin
11

Ich hatte Probleme mit dieser Ausnahme, während ich meine Anwendung ausführte.

Schließlich lag das Problem bei der SQL-Abfrage . Ich meine, dass die Abfrage falsch ist.

Bitte überprüfen Sie Ihre Anfrage. Das ist mein Vorschlag

Kumaresan Perumal
quelle
1
Zur Verdeutlichung: Wenn Sie 1. einen Fehler in Ihrer SQL-Syntax haben, 2. für das Rollback bei Ausnahme eingerichtet sind 3. ReadOnly-Transaktionen haben, wird dieser Fehler angezeigt, da die SQL-Syntax eine Ausnahme verursacht, die ein Rollback auslöst, das fehlschlägt, weil Sie sich in "befinden. schreibgeschützt "Modus.
Dave
6

Suchen Sie nach Ausnahmen, die in den ...Abschnitten Ihres Codes ausgelöst und abgefangen werden. Ausnahmen für Laufzeit- und Rollbacking-Anwendungen verursachen ein Rollback, wenn sie aus einer Geschäftsmethode entfernt werden, selbst wenn sie an einem anderen Ort abgefangen werden.

Mithilfe des Kontexts können Sie herausfinden, ob die Transaktion für das Rollback markiert ist.

@Resource
private SessionContext context;

context.getRollbackOnly();
Mareen
quelle
1
Mir scheint, ich habe die Ursache gefunden, aber ich verstehe nicht, warum dies geschieht. Eine innere Methode löst eine Ausnahme aus, die ich abfange, protokolliere und ignoriere. Die Transaktion wird jedoch ohnehin nur als Rollback markiert. Wie kann ich das verhindern? Ich möchte nicht, dass die Transaktionen von Ausnahmen beeinflusst werden, die ich richtig abfange.
Vojtěch
Ist SessionContexteine Standardklasse im Frühling? Mir scheint, es ist eher EJB3 und es ist nicht in meiner Frühlingsanwendung enthalten.
Vojtěch
3
Mein schlechtes Ich habe die Tatsache vermisst, dass es um den Frühling geht. Auf jeden Fall sollte so etwas wie TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()verfügbar sein.
Mareen
2

Eine gute Erklärung mit Lösungen gefunden: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/

1) Entfernen Sie @Transacional aus der verschachtelten Methode, wenn keine Transaktionssteuerung erforderlich ist. Selbst wenn es eine Ausnahme gibt, sprudelt es nur und hat keinen Einfluss auf das Transaktionsmaterial.

ODER:

2) Wenn eine verschachtelte Methode eine Transaktionssteuerung benötigt, machen Sie sie auf diese Weise zu REQUIRE_NEW für die Weitergaberichtlinie. Selbst wenn eine Ausnahme ausgelöst und nur als Rollback markiert wird, ist der Aufrufer nicht betroffen.

Aquajach
quelle
1

Deaktivieren Sie den Transaktionsmanager in Ihrer 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>

Wenn Sie diese Zeilen auskommentieren, wird die Ausnahme angezeigt, die den Rollback verursacht.

rémy
quelle
0

Wenden Sie den folgenden Code in productRepository an

@Query("update Product set prodName=:name where prodId=:id ") @Transactional @Modifying int updateMyData(@Param("name")String name, @Param("id") Integer id);

Während des Junit-Tests den folgenden Code anwenden

@Test
public void updateData()
{
  int i=productRepository.updateMyData("Iphone",102);

  System.out.println("successfully updated ... ");
  assertTrue(i!=0);

}

Es funktioniert gut für meinen Code

Asif Raza
quelle