Guten Tag an alle!
Nun, das Monatsende ist immer intensiv und es bleibt nur noch ein Tag bis zum Beginn des zweiten Streams des Kurses
„Developer on Spring Framework“ , eines wunderbaren und interessanten Kurses, der von dem ebenso schönen wie wütenden
Yuri unterrichtet wird (wie ihn einige Studenten wegen der Anforderungen nennen) in DZ), schauen wir uns also ein anderes Material an, das wir für Sie vorbereitet haben.
Lass uns gehen.
EinführungIn den meisten Fällen legen Entwickler keinen Wert auf das Transaktionsmanagement. Infolgedessen muss entweder der größte Teil des Codes später neu geschrieben werden, oder der Entwickler implementiert das Transaktionsmanagement, ohne zu wissen, wie es tatsächlich funktionieren sollte oder welche Aspekte speziell in seinem Fall verwendet werden sollten.
Ein wichtiger Aspekt bei der Transaktionsverwaltung ist die Festlegung der korrekten Grenzen einer Transaktion, wann eine Transaktion beginnen und wann sie enden soll, wann Daten zur Datenbank hinzugefügt und wann sie zurückgepumpt werden sollen (im Falle einer Ausnahme).

Der wichtigste Aspekt für Entwickler ist zu verstehen, wie das Transaktionsmanagement am besten in einer Anwendung implementiert werden kann. Schauen wir uns also die verschiedenen Optionen an.
TransaktionsmanagementmethodenTransaktionen können auf folgende Arten verwaltet werden:
1. Programmieren Sie die Steuerung, indem Sie benutzerdefinierten Code schreibenDies ist eine alte Transaktionsverwaltungsmethode.
EntityManagerFactory factory = Persistence.createEntityManagerFactory("PERSISTENCE_UNIT_NAME"); EntityManager entityManager = entityManagerFactory.createEntityManager(); Transaction transaction = entityManager.getTransaction() try { transaction.begin(); someBusinessCode(); transaction.commit(); } catch(Exception ex) { transaction.rollback(); throw ex; }
Vorteile :
- Transaktionsgrenzen sind im Code offensichtlich.
Nachteile :
- Es wiederholt sich und ist fehleranfällig.
- Jeder Fehler kann sehr große Auswirkungen haben.
- Sie müssen viele Vorlagen schreiben. Wenn Sie eine andere Methode von dieser Methode aus aufrufen möchten, müssen Sie sie erneut über den Code steuern.
2. Verwenden von Spring zur TransaktionsverwaltungSpring unterstützt zwei Arten der Transaktionsverwaltung
1. Software Transaction Management : Sie müssen Transaktionen durch Programmierung verwalten. Diese Methode ist flexibel genug, aber schwer zu warten.
2. Deklaratives Transaktionsmanagement : Sie trennen das Transaktionsmanagement von der Geschäftslogik. Sie verwenden Anmerkungen in der XML-basierten Konfiguration nur für die Transaktionsverwaltung.
Wir empfehlen dringend die Verwendung deklarativer Transaktionen. Wenn Sie die Gründe kennen möchten, lesen Sie weiter, andernfalls gehen Sie direkt zum Abschnitt Deklaratives Transaktionsmanagement, wenn Sie diese Option implementieren möchten.Schauen wir uns nun jeden Ansatz im Detail an.
2.1. Programmatisches Transaktionsmanagement:Das Spring-Framework bietet zwei Tools für das programmatische Transaktionsmanagement.
a. Verwenden von
TransactionTemplate
(vom Spring-Team empfohlen):
Schauen wir uns anhand des folgenden Beispielcodes an, wie dieser Typ implementiert wird (mit einigen Änderungen aus der Spring-Dokumentation).
Beachten Sie, dass die Codefragmente aus Spring Docs stammen.Kontext-XML-Datei:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/TEST"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="serviceImpl" class="com.service.ServiceImpl"> <constructor-arg ref="transactionManager"/> </bean>
Service
:
public class ServiceImpl implements Service { private final TransactionTemplate transactionTemplate;
Wenn es keinen Rückgabewert gibt, verwenden Sie die praktische
TransactionCallbackWithoutResult
Klasse mit einer anonymen Klasse, wie unten gezeigt:
transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { updateOperation1(); updateOperation2(); } });
- Instanzen der
TransactionTemplate
Klasse sind threadsicher, sodass nicht alle Dialogstatus unterstützt werden. TransactionTemplate
Instanzen unterstützen dennoch den Konfigurationsstatus. Wenn eine Klasse also eine TransactionTemplate mit unterschiedlichen Einstellungen (z. B. einer anderen Isolationsstufe) verwenden muss, müssen Sie zwei verschiedene TransactionTemplate-Instanzen erstellen, obwohl einige Klassen möglicherweise dieselbe TransactionTemplate-Instanz verwenden.
b. Direkte Verwendung der
PlatformTransactionManager
Implementierung:
Schauen wir uns diese Option noch einmal im Code an.
<!-- Initialization for data source --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/TEST"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <!-- Initialization for TransactionManager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> public class ServiceImpl implements Service { private PlatformTransactionManager transactionManager; public void setTransactionManager( PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } DefaultTransactionDefinition def = new DefaultTransactionDefinition();
Bevor wir mit der nächsten Methode der Transaktionsverwaltung fortfahren, wollen wir uns ansehen, wie Sie entscheiden, welche Art der Transaktionsverwaltung Sie wählen möchten.
Wahl zwischen
programmatischem und
deklarativem Transaktionsmanagement :
- Programmatisches Transaktionsmanagement ist nur dann eine gute Wahl, wenn Sie nur eine geringe Anzahl von Transaktionsvorgängen haben. (In den meisten Fällen handelt es sich nicht um Transaktionen.)
- Ein Transaktionsname kann nur in Program Transaction Management explizit festgelegt werden.
- Das programmatische Transaktionsmanagement sollte verwendet werden, wenn Sie das Transaktionsmanagement explizit steuern möchten.
- Wenn Ihre Anwendung jedoch zahlreiche Transaktionsvorgänge enthält, lohnt es sich, die deklarative Verwaltung zu verwenden.
- Die deklarative Verwaltung ermöglicht Ihnen nicht die Verwaltung von Transaktionen in der Geschäftslogik und ist nicht schwer zu konfigurieren.
2.2. Deklarative Transaktionen (werden normalerweise in fast allen Szenarien einer Webanwendung verwendet)Schritt 1 : Definieren Sie den Transaktionsmanager in der Kontext-XML-Datei Ihrer Spring-Anwendung.
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"/> <tx:annotation-driven transaction-manager="txManager"/>
Schritt 2 : Aktivieren Sie die Annotationsunterstützung, indem Sie einen Eintrag in die Kontext-XML-Datei Ihrer Spring-Anwendung einfügen.
ODER fügen
@EnableTransactionManagement
Ihrer Konfigurationsdatei
@EnableTransactionManagement
hinzu, wie unten gezeigt:
@Configuration @EnableTransactionManagement public class AppConfig { ... }
Spring empfiehlt, nur bestimmte Klassen (und Methoden bestimmter Klassen) mit @Transactional
Annotation zu @Transactional
verglichen mit Annotationsschnittstellen.Der Grund dafür ist, dass Sie die Annotation auf Schnittstellenebene platzieren. Wenn Sie Proxy-Klassen (
proxy-target-class = «true»
mode = «aspectj»
proxy-target-class = «true»
) oder den Verflechtungsaspekt (
mode = «aspectj»
) verwenden, werden die Transaktionsparameter von der Proxy-Infrastruktur nicht erkannt und Plexus, zum Beispiel Transaktionsverhalten wird nicht angewendet.
Schritt 3 : Fügen
@Transactional
der Klasse (Klassenmethode) oder der Schnittstelle (Schnittstellenmethode) die Annotation
@Transactional
.
<tx:annotation-driven proxy-target-class="true">
Standardkonfiguration:
proxy-target-class="false"
@Transactional
kann vor einer Schnittstellendefinition, einer Schnittstellenmethode, einer Klassendefinition oder einer öffentlichen Klassenmethode platziert werden.- Wenn einige Klassenmethoden (mit
@Transactional
Annotation gekennzeichnet) unterschiedliche Attributeinstellungen haben sollen, z. B. Isolationsstufe oder Ausbreitungsstufe, platzieren Sie die Annotation auf Methodenebene, um Attributeinstellungen auf Klassenebene zu überschreiben. - Im Proxy-Modus (der standardmäßig festgelegt ist) können nur "externe" Methodenaufrufe abgefangen werden, die den Proxy durchlaufen. Dies bedeutet, dass ein „unabhängiger Aufruf“, beispielsweise eine Methode im Ziel, die eine andere Methode des Ziels aufruft, zur Laufzeit nicht zu einer tatsächlichen Transaktion führt, selbst wenn die aufgerufene Methode mit
@Transactional
.
@Transactional
wir nun
@Transactional
Unterschied zwischen den Annotationsattributen
@Transactional
@Transactional (isolation=Isolation.READ_COMMITTED)
- Der Standardwert ist
Isolation.DEFAULT
- In den meisten Fällen verwenden Sie die Standardeinstellungen, bis Sie spezielle Anforderungen haben.
- Weist den Transaktionsmanager (
tx
) an, dass die nächste Isolationsstufe für den aktuellen tx
. Es muss an dem Punkt installiert werden, an dem tx
startet, da wir die Isolationsstufe nach dem Start von tx nicht ändern können.
STANDARD : Verwenden Sie die Standardisolationsstufe in der Basisdatenbank.
READ_COMMITTED (Lesen fester Daten): Eine Konstante, die angibt, dass ein
fehlerhaftes Lesen verhindert wurde. Nicht wiederholtes Lesen und Phantomlesen können auftreten.
READ_UNCOMMITTED (nicht
festgeschriebene Daten lesen): Diese Isolationsstufe gibt an, dass eine Transaktion Daten lesen kann, die noch nicht von anderen Transaktionen gelöscht wurden.
REPEATABLE_READ (
Lesewiederholbarkeit ): Eine Konstante, die angibt, dass schmutziges Lesen und nicht wiederholbares Lesen verhindert werden. Möglicherweise wird eine Phantomablesung angezeigt.
SERIALISIERBAR : Permanent, was darauf hinweist, dass schmutziges Lesen, nicht wiederholbares Lesen und Phantomlesen verhindert werden.
Was bedeutet dieser Jargon: "schmutziges" Lesen, Phantomlesen oder wiederholtes Lesen?
- Dirty Read : Transaktion A schreibt. In der Zwischenzeit liest Transaktion "B" denselben Datensatz, bis Transaktion A abgeschlossen ist. Später entscheidet sich Transaktion A für ein Rollback, und jetzt haben wir Änderungen an Transaktion B, die nicht kompatibel sind. Dies ist eine schmutzige Lektüre. Transaktion B arbeitete auf Isolationsebene READ_UNCOMMITTED, sodass die von Transaktion A vorgenommenen Änderungen vor Abschluss der Transaktion gelesen werden konnten.
- Nicht wiederholbares Lesen : Transaktion "A" liest einige Datensätze. Dann schreibt die Transaktion "B" diesen Datensatz und schreibt ihn fest. Später liest Transaktion A denselben Datensatz erneut und erhält möglicherweise andere Werte, da Transaktion B Änderungen an diesem Datensatz vorgenommen und diese festgeschrieben hat. Dies ist eine sich nicht wiederholende Lesung.
- Phantom Read : Transaktion „A“ liest eine Reihe von Datensätzen. Währenddessen fügt die Transaktion "B" einen neuen Datensatz in dieselbe Zeile wie die Transaktion A ein. Später liest die Transaktion A denselben Bereich erneut und empfängt auch den Datensatz, den die Transaktion B gerade eingefügt hat. Dies ist ein Phantom-Lesevorgang: Die Transaktion hat eine Reihe von Datensätzen mehrmals abgerufen aus der Datenbank und erhielt verschiedene Ergebnismengen (mit Phantomdatensätzen).
@Transactional(timeout=60)
Der Standardwert ist das Standardzeitlimit für das zugrunde liegende Transaktionssystem.
Informiert den TX-Manager über die Wartezeit, bis TX gesendet wird, bevor entschieden wird, ob nicht antwortende Transaktionen zurückgesetzt werden sollen.
@Transactional(propagation=Propagation.REQUIRED)
Wenn nicht angegeben, ist das Standardausbreitungsverhalten
REQUIRED
.
Andere Optionen sind
REQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTED, NEVER
und
NESTED
.
ERFORDERLICHGibt an, dass die Zielmethode ohne aktiven Empfang nicht funktionieren kann. Wenn tx bereits ausgeführt wird, bevor diese Methode aufgerufen wird, wird es im selben tx fortgesetzt, oder der neue tx wird kurz nach dem Aufrufen dieser Methode gestartet.
REQUIRES_NEW- Gibt an, dass bei jedem Aufruf der Zielmethode ein neuer TX ausgeführt werden soll. Wenn tx bereits ausgeführt wird, wird es angehalten, bevor ein neues gestartet wird.
Mandat- Gibt an, dass für die Zielmethode aktives Senden erforderlich ist. Wenn tx nicht fortgesetzt wird, schlägt es nicht fehl und löst eine Ausnahme aus.
UNTERSTÜTZT- Gibt an, dass die Zielmethode unabhängig von tx ausgeführt werden kann. Wenn tx funktioniert, wird es am selben tx teilnehmen. Wenn es ohne tx ausgeführt wird, wird es weiterhin ausgeführt, wenn keine Fehler vorliegen.
- Methoden zum Abrufen von Daten sind die besten Kandidaten für diese Option.
NOT_SUPPORTED- Gibt an, dass für die Zielmethode keine Weitergabe des Transaktionskontexts erforderlich ist.
- Grundsätzlich sind die Methoden, die in einer Transaktion ausgeführt werden, aber Operationen mit RAM ausführen, die besten Kandidaten für diese Option.
Niemals- Gibt an, dass die Zielmethode eine Ausnahme auslöst, wenn sie in einem Transaktionsprozess ausgeführt wird.
- Diese Option wird in den meisten Fällen nicht in Projekten verwendet.
@Transactional (rollbackFor=Exception.class)
Standardwert:
rollbackFor=RunTimeException.class
Im Frühjahr lösen alle API-Klassen eine RuntimeException aus. Wenn also eine Methode fehlschlägt, setzt der Container die aktuelle Transaktion immer zurück.
Das Problem besteht nur mit aktivierten Ausnahmen. Daher kann dieser Parameter verwendet werden, um eine Transaktion deklarativ zurückzusetzen, wenn eine
Checked Exception
auftritt.
@Transactional (noRollbackFor=IllegalStateException.class)
Gibt an, dass kein Rollback stattfinden soll, wenn die Zielmethode diese Ausnahme auslöst.
Der letzte, aber wichtigste Schritt im Transaktionsmanagement ist das Veröffentlichen der Annotation
@Transactiona
l. In den meisten Fällen kommt es zu Verwirrung, wo sich die Anmerkung befinden sollte: auf Serviceebene oder auf DAO-Ebene?
@Transactional
: Service- oder DAO-Level?Service ist der beste Ort, um
@Transactional
. Die Serviceebene sollte das Verhalten des Anwendungsfalls auf der Detailebene für die Benutzerinteraktion enthalten, die logisch in die Transaktion
@Transactional
.
Es gibt viele CRUD-Anwendungen, die keine signifikante Geschäftslogik mit einem Service-Level haben, der einfach Daten zwischen Controllern und Datenzugriffsobjekten überträgt, was nicht nützlich ist. In diesen Fällen können wir die Transaktionsanmerkung auf DAO-Ebene setzen.
Daher können Sie sie in der Praxis überall platzieren, es liegt an Ihnen.
Wenn Sie
@Transactional
in die DAO-Schicht
@Transactional
und Ihre DAO-Schicht von verschiedenen Diensten wiederverwendet wird, ist es außerdem schwierig, sie in der DAO-Schicht zu platzieren, da unterschiedliche Dienste unterschiedliche Anforderungen haben können.
Wenn Ihr Service Level Objekte mit Hibernate abruft und Sie beispielsweise verzögerte Initialisierungen in der Definition eines Domänenobjekts haben, müssen Sie die Transaktion auf Service Level öffnen, da sonst eine von ORM ausgelöste LazyInitializationException auftritt.
Stellen Sie sich ein anderes Beispiel vor, in dem Ihr Service Level zwei verschiedene DAO-Methoden aufrufen kann, um Datenbankoperationen auszuführen. Wenn Ihre erste DAO-Operation fehlgeschlagen ist, werden möglicherweise die beiden anderen übertragen, und Sie beenden den inkonsistenten Status der Datenbank. Service Level Annotation kann Sie vor solchen Situationen bewahren.
Ich hoffe dieser Artikel hat dir geholfen.
DAS ENDE
Es ist immer interessant, Ihre Kommentare oder Fragen zu sehen.