Was ist der Unterschied zwischen der Definition von @Transactional für Klasse und Methode?

73

Fall 1

@Transactional
public class UserServiceImpl implements UserService {

    ...................
    public void method1(){
        try{
            method2();
        }catch(Exception e){

        }
    }
    public void method2(){

    }
}

Fall2

public class UserServiceImpl implements UserService {

    ...................
    public void method1(){
        try{
            method2();
        }catch(Exception e){

        }
    }
    @Transactional
    public void method2(){

    }
}

In Fall 1 funktioniert das Rollback, wenn eine Ausnahme auftritt, aber in Fall 2 nicht. Gibt es Leistungsprobleme, wenn ich dem Fall folge1?

Anil kumar
quelle

Antworten:

79

Im Fall 1 wird @Transactional auf jede öffentliche Einzelmethode angewendet. Private und geschützte Methoden werden von Spring ignoriert.

Spring wendet die Annotation auf Klassenebene auf alle öffentlichen Methoden dieser Klasse an, die wir nicht mit @Transactional annotiert haben. Wenn wir die Annotation jedoch auf eine private oder geschützte Methode setzen, ignoriert Spring sie fehlerfrei.

In Fall 2 wird @Transactional nur auf Methode2 () angewendet, nicht auf Methode1 ()

Fall 1: - Aufrufen von Methode1 () -> Eine Transaktion wird gestartet. Wenn method1 () method2 () aufruft, wird keine neue Transaktion gestartet, da bereits eine vorhanden ist

Fall 2: - Aufrufen von Methode1 () -> Es wird keine Transaktion gestartet. Wenn method1 () method2 () aufruft, wird KEINE neue Transaktion gestartet. Dies liegt daran, dass @Transactional nicht funktioniert, wenn eine Methode aus derselben Klasse aufgerufen wird. Es würde funktionieren, wenn Sie method2 () aus einer anderen Klasse aufrufen würden.

Aus dem Federreferenzhandbuch :

Im Proxy-Modus (Standardeinstellung) werden nur externe Methodenaufrufe abgefangen, die über den Proxy eingehen. Dies bedeutet, dass der Selbstaufruf einer Methode innerhalb des Zielobjekts, die eine andere Methode des Zielobjekts aufruft, zur Laufzeit nicht zu einer tatsächlichen Transaktion führt, selbst wenn die aufgerufene Methode mit @Transactional markiert ist. Außerdem muss der Proxy vollständig initialisiert sein, um das erwartete Verhalten bereitzustellen, sodass Sie sich in Ihrem Initialisierungscode, dh @PostConstruct, nicht auf diese Funktion verlassen sollten.

niekname
quelle
So verdoppeln Sie die Warnung, eine mit Anmerkungen versehene Methode von einer nicht mit Anmerkungen versehenen Methode aus aufzurufen: Selbst für die mit Anmerkungen versehene Methode wird KEINE Transaktion gestartet. Siehe auch Moks Antwort.
Kaicarno
41

@Transactionalon a class gilt für jede Methode im Service. Es ist eine Abkürzung. In der Regel können Sie @Transactional(readOnly = true)eine Serviceklasse festlegen , wenn Sie wissen, dass alle Methoden auf die Repository-Ebene zugreifen. Sie können das Verhalten dann mit @Transactionalon-Methoden überschreiben, die Änderungen in Ihrem Modell ausführen. Leistungsprobleme zwischen 1) und 2) sind nicht bekannt.

jeromerg
quelle
Wenn ich eine Klasse Transactional (value = "notPrimaryTransactionManager") mit Anmerkungen versehen und dann eine Mitgliedsmethode mit Transactional (readOnly = true) kommentiere, verwendet die Methode "notPrimaryTransactionManager" oder verwendet sie den vom Frühling bereitgestellten Standardtransaktionsmanager?
Anjil Dhamala
21

Angenommen, Sie haben die folgende Klasse:

@Transactional(readOnly = true)
public class DefaultFooService implements FooService {

  public Foo getFoo(String fooName) {
    // do something
  }

  // these settings have precedence for this method
  @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
  public void updateFoo(Foo foo) {
    // do something
  }
}

Die @TransactionalAnmerkung auf Klassenebene wird auf jede Methode in der Klasse angewendet.

Jedoch , wenn ein Verfahren mit Anmerkungen versehen ist @Transactional(wie updateFoo(Foo foo)) , wird dies auf der Klassenebene definierten Vorrang vor den Transaktions Einstellungen übernehmen.

Mehr Info:

Konstantin Yovkov
quelle
4
Aber nur, wenn updateFoo extern aufgerufen wird. Wenn getFoo updateFoo aufruft (wie im OP gefragt), wird die gesamte Transaktion schreibgeschützt.
David Newcomb
17

Zitat von hier

Das Spring-Team empfiehlt, dass Sie nur konkrete Klassen mit der Annotation @Transactional mit Anmerkungen versehen, anstatt Schnittstellen mit Anmerkungen zu versehen.

Da dieser Mechanismus auf Proxys basiert, werden nur 'externe' Methodenaufrufe abgefangen, die über den Proxy eingehen. Dies bedeutet, dass 'Selbstaufruf', dh eine Methode innerhalb des Zielobjekts, die eine andere Methode des Zielobjekts aufruft, zur Laufzeit nicht zu einer tatsächlichen Transaktion führt, selbst wenn die aufgerufene Methode mit @Transactional markiert ist!

mok
quelle
8
Dies zeigt nicht den Unterschied zwischen der mit @TransactionalAnmerkungen versehenen Methode und der mit @TransactionalAnmerkungen versehenen Klasse.
Konstantin Yovkov
1
@kocko> Ich dachte, das OP wird dem Link folgen, trotzdem habe ich gerade meine Antwort aktualisiert, bitte werfen Sie einen Blick darauf.
Mok
1
@mok Ich hatte das Problem "Selbstaufruf schafft keine Transaktion" und diese Antwort ließ mich an das Lernen erinnern, das ich vor einiger Zeit hatte. Vielen Dank!
Asgs
Dies erklärt nicht den Unterschied, den das OP gefragt hat, sondern hebt nur ein Problem hervor, mit dem die Methoden mit Anmerkungen versehen werden @Transactional. Die verknüpfte Dokumentation wird jetzt aktualisiert. Aus Gründen der Nachwelt heißt es in der neuesten Dokumentation: Das Spring-Team empfiehlt, nur konkrete Klassen (und Methoden konkreter Klassen) mit der Annotation @Transactional zu versehen, anstatt Schnittstellen mit Anmerkungen zu versehen.
Railomaya