Wohin gehört die Annotation @Transactional?

528

Sollten Sie die @Transactionalin die DAOKlassen und / oder deren Methoden einfügen oder ist es besser, die Serviceklassen zu kommentieren, die mit den DAO-Objekten aufrufen? Oder ist es sinnvoll, beide "Ebenen" mit Anmerkungen zu versehen?

Thomas Einwaller
quelle

Antworten:

537

Ich denke, Transaktionen gehören zur Service-Schicht. Es ist derjenige, der über Arbeitseinheiten und Anwendungsfälle Bescheid weiß. Dies ist die richtige Antwort, wenn mehrere DAOs in einen Service eingefügt wurden, die in einer einzigen Transaktion zusammenarbeiten müssen.

Duffymo
quelle
Ich stimme dem zu. Manchmal spielt es keine Rolle, aber manchmal können Sie davon profitieren, z. B. wird die Hibernate-Sitzung für die while-Transaktion überspannt, sodass sich alle geladenen Objekte im Cache der ersten Ebene befinden und Sie die Objekte nicht erneut an die Sitzung anhängen müssen und nicht träge geladen werden müssen Eigenschaften funktionieren ohne Flaum.
dma_k
5
Kann eine globale Transaktion aus mehr als einer dieser @Transactional-Transaktionen bestehen (Propagation = Propagation.REQUIRED)? Oder ist @Transactional immer eine Grenze für eine Transaktion? Ich bin nicht sicher, ob ich es aus der Dokumentation erhalten habe, aber es scheint ein bisschen, dass Sie sogar Transaktionen erstellen können, die aus @ Transactional-Methoden und allem, was darin läuft, bestehen
lisak
1
Ja, ich denke, das äußerste @Transactional ist dasjenige, das zur Grenze für die Transaktion wird, wenn Propagation.REQUIRED aktiviert ist.
Duffymo
309

Im Allgemeinen stimme ich den anderen zu, dass Transaktionen normalerweise auf Service-Ebene gestartet werden (abhängig von der Granularität, die Sie natürlich benötigen).

In der Zwischenzeit habe ich jedoch auch begonnen @Transactional(propagation = Propagation.MANDATORY), meine DAO-Ebene (und andere Ebenen, die keine Transaktionen starten dürfen, aber vorhandene erfordern) hinzuzufügen , da es viel einfacher ist, Fehler zu erkennen, bei denen Sie vergessen haben, eine Transaktion im Aufrufer zu starten ( zB der Service). Wenn Ihr DAO mit einer obligatorischen Weitergabe versehen ist, wird eine Ausnahme angezeigt, die besagt, dass beim Aufrufen der Methode keine aktive Transaktion vorhanden ist.

Ich habe auch einen Integrationstest, bei dem ich alle Beans (Bean-Postprozessor) auf diese Annotation überprüfe und fehlschlage, wenn @Transactionalin einer Bean, die nicht zur Serviceschicht gehört, eine Annotation mit einer anderen als der obligatorischen Weitergabe vorhanden ist . Auf diese Weise stelle ich sicher, dass wir keine Transaktionen auf der falschen Ebene starten.

mnp
quelle
3
Sollte dies in der Dao-Ebene (Schnittstellen) erfolgen, wird in der Dao-Impl. Schicht oder beides?
Johan
3
Ich weiß nicht, ob es in diese Diskussion passt, aber ein weiterer Tipp könnte darin bestehen, @Transactional (readOnly = true) in das Dao-Impl in den nicht schreibenden Operationen einzufügen.
Maxivis
10
@Johan Spring empfiehlt, die Transaktionsanmerkungen anstelle der Schnittstellen in die Implementierungsklassen einzufügen.
Mathias G.
Ich mag diese Idee wirklich und versuche sie auch für meine Projekte
Thomas Einwaller
1
Mal sehen, ob ich verstehe ... sagen Sie, ich sollte @Transactionaldie Service-Implementierungsklasse und @Transactional(propagation = MANDATORY)die DAO-Klassenimplementierung (Repository) aktivieren?
John Henckel
93

Transaktionsanmerkungen sollten um alle Vorgänge platziert werden, die untrennbar miteinander verbunden sind.

Ihr Anruf lautet beispielsweise "Passwort ändern". Das besteht aus zwei Operationen

  1. Änder das Passwort.
  2. Überprüfen Sie die Änderung.
  3. Senden Sie dem Client eine E-Mail, dass sich das Kennwort geändert hat.

Wenn die Prüfung oben fehlschlägt, sollte dann auch die Kennwortänderung fehlschlagen? Wenn ja, sollte die Transaktion bei 1 und 2 liegen (also auf der Serviceebene). Wenn die E-Mail fehlschlägt (wahrscheinlich sollte eine Art Fail-Safe vorhanden sein, damit sie nicht fehlschlägt), sollte sie dann das Änderungskennwort und die Prüfung zurücksetzen?

Dies sind die Fragen, die Sie stellen müssen, wenn Sie entscheiden, wo Sie die platzieren möchten @Transactional.

Michael Wiles
quelle
41

Die richtige Antwort für traditionelle Spring-Architekturen besteht darin, Transaktionssemantik auf die Serviceklassen zu setzen, aus den Gründen, die andere bereits beschrieben haben.

Ein aufkommender Trend im Frühjahr geht zum domänengetriebenen Design (DDD). Spring Roo ist ein gutes Beispiel für diesen Trend. Die Idee ist, die POJOs des Domänenobjekts viel umfangreicher zu machen als bei typischen Spring-Architekturen (normalerweise sind sie anämisch ), und insbesondere die Transaktions- und Persistenzsemantik auf die Domänenobjekte selbst anzuwenden. In Fällen, in denen lediglich einfache CRUD-Operationen erforderlich sind, arbeiten die Webcontroller direkt mit den POJOs des Domänenobjekts (sie fungieren in diesem Kontext als Entitäten), und es gibt keine Serviceebene. In Fällen, in denen eine Art Koordination zwischen Domänenobjekten erforderlich ist, können Sie ein Service-Bean-Handle verwenden, mit dem@Transactionnach Tradition. Sie können die Transaktionsweitergabe für die Domänenobjekte REQUIREDso einstellen , dass die Domänenobjekte alle vorhandenen Transaktionen verwenden, z. B. Transaktionen, die an der Service-Bean gestartet wurden.

Technisch nutzt diese Technik AspectJ und <context:spring-configured />. Roo verwendet AspectJ-Definitionen zwischen Typen, um die Entitätssemantik (Transaktionen und Persistenz) vom Domänenobjektmaterial (im Wesentlichen Felder und Geschäftsmethoden) zu trennen.

naXa
quelle
38

Der normale Fall wäre das Kommentieren auf Service-Layer-Ebene, dies hängt jedoch wirklich von Ihren Anforderungen ab.

Das Annotieren auf einer Serviceebene führt zu längeren Transaktionen als das Annotieren auf DAO-Ebene. Abhängig von der Transaktionsisolationsstufe, die Probleme verursachen kann, werden bei gleichzeitigen Transaktionen die Änderungen des anderen nicht z. WIEDERHOLBAR LESEN.

Durch das Kommentieren der DAOs werden die Transaktionen so kurz wie möglich gehalten, mit dem Nachteil, dass die Funktionalität, die Ihre Service-Schicht bereitstellt, nicht in einer einzigen (rollbackbaren) Transaktion ausgeführt wird.

Es ist nicht sinnvoll, beide Ebenen mit Anmerkungen zu versehen, wenn der Ausbreitungsmodus auf Standard eingestellt ist.

Hammarback
quelle
31

Ich platziere das @Transactionalauf der @ServiceEbene und setze rollbackForjede Ausnahme und readOnlyum die Transaktion weiter zu optimieren.

Standardmäßig @Transactionalwird nur nach RuntimeException(nicht aktivierten Ausnahmen) gesucht. Wenn Sie das Rollback auf Exception.class(Überprüfte Ausnahmen) setzen, wird das Rollback für jede Ausnahme ausgeführt.

@Transactional(readOnly = false, rollbackFor = Exception.class)

Siehe Überprüfte und nicht aktivierte Ausnahmen .

user2601995
quelle
17

Oder ist es sinnvoll, beide "Ebenen" mit Anmerkungen zu versehen? - Ist es nicht sinnvoll, sowohl die Service-Schicht als auch die Dao-Schicht mit Anmerkungen zu versehen? - Wenn sichergestellt werden soll, dass die DAO-Methode immer von einer Service-Schicht aufgerufen (weitergegeben) wird, deren Weitergabe in DAO "obligatorisch" ist. Dies würde eine gewisse Einschränkung für DAO-Methoden darstellen, die von der UI-Schicht (oder den Controllern) aufgerufen werden können. Wenn insbesondere die DAO-Schicht getestet wird, wird durch das Annotieren von DAO auch sichergestellt, dass die Transaktionsfunktionalität getestet wird.

Tweekran
quelle
Würde dies nicht zu verschachtelten Transaktionen führen? Und all die subtilen Probleme, die damit einhergehen?
HDave
11
Nein, in JPA gibt es keine verschachtelten Transaktionen. Es wäre vollkommen in Ordnung, sie in beide zu setzen. Wenn Sie sich bereits in einer Transaktion befinden, als Sie das DAO erreichen, wird diese Transaktion fortgesetzt.
narr4jesus
4
Bei Verwendung kann eine verschachtelte Transaktion auftreten propagation=Propagation.REQUIRES_NEW. Andernfalls nimmt das DAO in den meisten Fällen, einschließlich Propagierung = obligatorisch, nur an der vorhandenen Transaktion teil, die von der Serviceschicht gestartet wurde.
Dan Carter
15

Für Transaktionen auf Datenbankebene

Meistens habe ich @Transactionalin DAOs nur auf Methodenebene verwendet, daher kann die Konfiguration speziell für eine Methode erfolgen / Standard verwenden (erforderlich)

  1. Die DAO-Methode, mit der Daten abgerufen werden (select ..), die nicht benötigt @Transactional wird, kann zu einem gewissen Overhead führen, da auch der Transaktions-Interceptor / und der AOP-Proxy ausgeführt werden müssen.

  2. DAOs Methoden, die einfügen / aktualisieren, werden erhalten @Transactional

sehr guter Blog über Transctional

Für die Anwendungsebene -
Ich verwende die Transaktionsfunktion für die Geschäftslogik. Ich möchte in der Lage sein, im Falle eines unerwarteten Fehlers ein Rollback durchzuführen

@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){

    try {    
        //service logic here     
    } catch(Throwable e) {

        log.error(e)
        throw new MyApplicationException(..);
    }
}
lukass77
quelle
6
+1 sehr schöner Artikel über TransactionalinJava
Eiche
11

Normalerweise sollte man eine Transaktion auf der Service-Ebene platzieren.

Wie bereits erwähnt, sagt uns die Atomizität einer Operation, wo eine Anmerkung erforderlich ist. Wenn Sie also Frameworks wie Hibernate verwenden, bei denen eine einzelne Operation "Speichern / Aktualisieren / Löschen / ... Ändern" für ein Objekt mehrere Zeilen in mehreren Tabellen ändern kann (aufgrund der Kaskade durch das Objektdiagramm), von Natürlich sollte es auch ein Transaktionsmanagement für diese spezielle DAO-Methode geben.

yannick555
quelle
10

@TransactionalAlle untrennbaren Vorgänge sollten mit Anmerkungen versehen werden. Die Verwendung der @TransactionalTransaktionsweitergabe wird automatisch behandelt. Wenn in diesem Fall eine andere Methode von der aktuellen Methode aufgerufen wird, hat diese Methode die Möglichkeit, der laufenden Transaktion beizutreten.

Nehmen wir also ein Beispiel:

Wir haben 2 Modelle, dh Countryund City. Relationales Mapping von Countryund CityModell ist so, als ob man Countrymehrere Städte haben kann, also ist Mapping wie folgt:

@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;

Hier wurde Country mehreren Städten zugeordnet, um sie abzurufen Lazily. So kommt hier Rolle , @Transactinalwenn wir abrufen Land Objekt aus der Datenbank , dann werden wir alle die Daten von Land Objekt erhalten , aber nicht Set von Städten bekommen , weil wir Städte zu holen LAZILY.

//Without @Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //After getting Country Object connection between countryRepository and database is Closed 
}

Wenn wir vom Länderobjekt aus auf Set of Cities zugreifen möchten, erhalten wir Nullwerte in diesem Set, da das Objekt von Set, das nur für dieses Set erstellt wurde, nicht mit den Daten initialisiert wird, um die von uns verwendeten Set-Werte abzurufen, @Transactionald. H.

//with @Transactional
@Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
   Object object = country.getCities().size();   
}

Grundsätzlich @Transactionalkann der Service mehrere Anrufe in einer einzigen Transaktion tätigen, ohne die Verbindung zum Endpunkt zu schließen.

Harshal Patil
quelle
2
sehr informativ, danke! Genau das, wonach ich gesucht habe, eine @Transactional
Erklärung dessen,
6

Es ist besser, es in der Service-Schicht zu haben! Dies wird in einem Artikel, den ich gestern gesehen habe, deutlich erklärt! Hier ist der Link , den Sie überprüfen können!

Sundary
quelle
5

Geben Sie hier die Bildbeschreibung ein

Das @Transactionalsollte auf der Serviceschicht verwendet werden, da es die Geschäftslogik enthält. Die DAO-Schicht verfügt normalerweise nur über Datenbank-CRUD-Operationen.

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);
}

Frühlingsdokument: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html

Alin
quelle
5

Die Service-Schicht ist der beste Ort, um @TransactionalAnmerkungen hinzuzufügen, da der größte Teil der hier vorhandenen Geschäftslogik das Anwendungsfallverhalten auf Detailebene enthält.

Angenommen, wir fügen es DAO hinzu und rufen vom Dienst aus zwei DAO-Klassen auf, eine fehlgeschlagene und eine andere erfolgreiche. In diesem Fall wird @Transactionaleine Datenbank festgeschrieben und eine andere zurückgesetzt, wenn sie nicht im Dienst ist.

Daher empfehle ich, diese Anmerkung mit Bedacht zu verwenden und nur auf Serviceebene zu verwenden.

Github-Projekt - Java-Algen

VishalTambe
quelle
Ausnahmen wie ObjectOptimisticLockingFailureException treten erst nach Abschluss der Transaktion auf. Wenn Sie separate Threads für andere Vorgänge wie den Mail-Dienst haben, schlägt dieses Design völlig fehl. Wir leiden gerade. Die einzige verbleibende Lösung wäre AOP.
Nabin Kumar Khatiwada
4

Lassen Sie uns zunächst definieren, wo wir die Transaktion verwenden müssen .

Ich denke, die richtige Antwort ist - wenn wir sicherstellen müssen, dass die Abfolge der Aktionen zusammen als eine atomare Operation beendet wird oder keine Änderungen vorgenommen werden, selbst wenn eine der Aktionen fehlschlägt.

Es ist allgemein bekannt, Geschäftslogik in Dienste zu integrieren. Daher können Servicemethoden verschiedene Aktionen enthalten, die als eine einzige logische Arbeitseinheit ausgeführt werden müssen. Wenn ja, muss diese Methode als Transaktion markiert werden . Natürlich erfordert nicht jede Methode eine solche Einschränkung, sodass Sie nicht den gesamten Service als transaktional markieren müssen .

Und noch mehr - vergessen Sie nicht zu berücksichtigen, dass @Transactional offensichtlich die Methodenleistung beeinträchtigen kann. Um das ganze Bild zu sehen, müssen Sie die Transaktionsisolationsstufen kennen. Wenn Sie wissen, dass Sie @Transactional möglicherweise nicht dort verwenden, wo es nicht unbedingt benötigt wird.

smaiakov
quelle
3

Es ist besser, @Transactional in einer separaten mittleren Schicht zwischen DAO und Service-Schicht zu halten. Da Rollback sehr wichtig ist, können Sie Ihre gesamte DB-Manipulation in der mittleren Schicht platzieren und Geschäftslogik in die Service-Schicht schreiben. Die mittlere Ebene interagiert mit Ihren DAO-Ebenen.

Dies hilft Ihnen in vielen Situationen wie ObjectOptimisticLockingFailureException - Diese Ausnahme tritt erst auf, nachdem Ihre Transaktion beendet ist. Sie können es also nicht in der mittleren Schicht abfangen, aber Sie können es jetzt in Ihrer Service-Schicht abfangen. Dies wäre nicht möglich, wenn Sie @Transactional in der Service-Schicht haben. Sie können zwar in Controller fangen, aber Controller sollte so sauber wie möglich sein.

Wenn Sie nach Abschluss aller Optionen zum Speichern, Löschen und Aktualisieren E-Mails oder SMS in einem separaten Thread senden, können Sie dies im Dienst tun, nachdem die Transaktion in Ihrer mittleren Ebene abgeschlossen wurde. Wenn Sie @Transactional in der Serviceschicht erwähnen, werden Ihre E-Mails auch dann gesendet, wenn Ihre Transaktion fehlschlägt.

Eine mittlere @ Transaction-Ebene hilft also dabei, Ihren Code besser und einfacher zu handhaben. Andernfalls können Sie bei Verwendung in der DAO-Ebene möglicherweise nicht alle Vorgänge rückgängig machen. Wenn Sie in der Service-Schicht verwenden, müssen Sie in bestimmten Fällen möglicherweise AOP (Aspect Oriented Programming) verwenden.

Nabin Kumar Khatiwada
quelle
2

Im Idealfall repräsentiert die Service-Schicht (Manager) Ihre Geschäftslogik und sollte daher mit @Transactional.Service-Schicht kann unterschiedliche DAO aufrufen, um DB-Operationen auszuführen. Nehmen wir eine Situation an, in der Sie N Anzahl von DAO-Operationen in einer Servicemethode haben. Wenn Ihre erste DAO-Operation fehlgeschlagen ist, werden möglicherweise noch andere übergeben, und Sie erhalten einen inkonsistenten DB-Status. Das Kommentieren der Service-Schicht kann Sie vor solchen Situationen bewahren.

Salsinga
quelle
1

Ich bevorzuge die Verwendung @Transactionalauf Services-Ebene auf Methodenebene.

Spark-Anfänger
quelle
1

@TransactionalVerwendet in der Serviceschicht, die unter Verwendung der Controller-Schicht ( @Controller) aufgerufen wird, und der Service-Schicht, die die DAO-Schicht ( @Repository) aufruft, dh eine datenbankbezogene Operation.

saß
quelle