Sollten Sie die @Transactional
in die DAO
Klassen 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?
Sollten Sie die @Transactional
in die DAO
Klassen 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?
Antworten:
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.
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 @Transactional
in 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.
@Transactional
die Service-Implementierungsklasse und @Transactional(propagation = MANDATORY)
die DAO-Klassenimplementierung (Repository) aktivieren?
Transaktionsanmerkungen sollten um alle Vorgänge platziert werden, die untrennbar miteinander verbunden sind.
Ihr Anruf lautet beispielsweise "Passwort ändern". Das besteht aus zwei Operationen
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
.
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@Transaction
nach Tradition. Sie können die Transaktionsweitergabe für die Domänenobjekte REQUIRED
so 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.
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.
Ich platziere das @Transactional
auf der @Service
Ebene und setze rollbackFor
jede Ausnahme und readOnly
um die Transaktion weiter zu optimieren.
Standardmäßig @Transactional
wird 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)
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.
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.
Außerdem empfiehlt Spring, die Annotation nur für konkrete Klassen und nicht für Schnittstellen zu verwenden.
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
Für Transaktionen auf Datenbankebene
Meistens habe ich @Transactional
in DAOs nur auf Methodenebene verwendet, daher kann die Konfiguration speziell für eine Methode erfolgen / Standard verwenden (erforderlich)
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.
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(..);
}
}
Transactional
inJava
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.
@Transactional
Alle untrennbaren Vorgänge sollten mit Anmerkungen versehen werden. Die Verwendung der @Transactional
Transaktionsweitergabe 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 Country
und City
. Relationales Mapping von Country
und City
Modell ist so, als ob man Country
mehrere 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 , @Transactinal
wenn 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, @Transactional
d. 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 @Transactional
kann der Service mehrere Anrufe in einer einzigen Transaktion tätigen, ohne die Verbindung zum Endpunkt zu schließen.
@Transactional
Das @Transactional
sollte 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
Die Service-Schicht ist der beste Ort, um @Transactional
Anmerkungen 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 @Transactional
eine 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.
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.
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.
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.
Ich bevorzuge die Verwendung @Transactional
auf Services-Ebene auf Methodenebene.