Programmatische Transaktionen im Spring TestContext Framework

Programmatische Transaktionen im Spring TestContext Framework

1. Einführung

Spring bietet eine hervorragende Unterstützung für das deklarative Transaktionsmanagement inapplication code sowie inintegration tests.

Es kann jedoch gelegentlich erforderlich sein, die Transaktionsgrenzen genau zu steuern.

In diesem Artikel sehen wirhow to programmatically interact with automatic transactions set up by Spring in transactional tests.

2. Voraussetzungen

Nehmen wir an, wir haben einige Integrationstests in unserer Spring-Anwendung.

Insbesondere erwägen wir Tests, die mit einer Datenbank interagieren. Überprüfen Sie beispielsweise, ob sich unsere Persistenzschicht korrekt verhält.

Betrachten wir eine Standardtestklasse - kommentiert als Transaktionsklasse:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { HibernateConf.class })
@Transactional
public class HibernateBootstrapIntegrationTest { ... }

In einem solchen Test werdenevery test method is wrapped in a transaction, which gets rolled back when the method exits.

Es ist natürlich auch möglich, nur bestimmte Methoden mit Anmerkungen zu versehen. Alles, was wir in diesem Artikel diskutieren, gilt auch für dieses Szenario.

3. DieTestTransaction Klasse

Wir werden den Rest des Artikels damit verbringen, eine einzelne Klasse zu diskutieren:org.springframework.test.context.transaction.TestTransaction.

Dies ist eine Utility-Klasse mit einigen statischen Methoden, mit denen wir in unseren Tests mit Transaktionen interagieren können.

Jede Methode interagiert mit der einzigen aktuellen Transaktion, die während der Ausführung einer Testmethode ausgeführt wird.

3.1. Überprüfen des Status der aktuellen Transaktion

Eine Sache, die wir in Tests oft tun, ist zu überprüfen, ob die Dinge in dem Zustand sind, den sie sein sollen.

Aus diesem Grund möchten wir möglicherweise prüfen, ob derzeit eine Transaktion aktiv ist:

assertTrue(TestTransaction.isActive());

Oder es könnte uns interessieren, ob die aktuelle Transaktion für ein Rollback markiert ist oder nicht:

assertTrue(TestTransaction.isFlaggedForRollback());

Wenn dies der Fall ist, wird Spring das Rollback entweder automatisch oder programmgesteuert ausführen, bevor es endet. Andernfalls wird es kurz vor dem Schließen festgeschrieben.

3.2. Kennzeichnen einer Transaktion für Commit oder Rollback

Wir können die Richtlinie programmgesteuert so ändern, dass die Transaktion festgeschrieben oder zurückgesetzt wird, bevor sie geschlossen wird:

TestTransaction.flagForCommit();
TestTransaction.flagForRollback();

Normalerweise werden Transaktionen in Tests beim Start für ein Rollback gekennzeichnet. Wenn die Methode jedoch eine@Commit-Annotation hat, werden sie stattdessen für das Festschreiben markiert:

@Test
@Commit
public void testFlagForCommit() {
    assertFalse(TestTransaction.isFlaggedForRollback());
}

Beachten Sie, dass diese Methoden die Transaktion lediglich kennzeichnen, wie ihre Namen implizieren. Das heißt,the transaction isn’t committed or rolled back immediately, but only just before it ends.

3.3. Starten und Beenden einer Transaktion

Um eine Transaktion festzuschreiben oder zurückzusetzen, lassen wir die Methode entweder beenden oder beenden sie explizit:

TestTransaction.end();

Wenn wir später wieder mit der Datenbank interagieren möchten, müssen wir eine neue Transaktion starten:

TestTransaction.start();

Beachten Sie, dass die neue Transaktion gemäß der Standardeinstellung der Methode für ein Rollback (oder Commit) markiert wird. Mit anderen Worten, frühere Aufrufe vonflagFor…haben keine Auswirkungen auf neue Transaktionen.

4. Einige Implementierungsdetails

TestTransaction ist nichts Magisches. Wir werden uns nun die Implementierung ansehen, um mehr über Transaktionen in Tests mit Spring zu erfahren.

Wir können sehen, dass seine wenigen Methoden einfach Zugriff auf die aktuelle Transaktion erhalten und einen Teil seiner Funktionalität kapseln.

4.1. Woher beziehtTestTransaction die aktuelle Transaktion?

Gehen wir direkt zum Code:

TransactionContext transactionContext
  = TransactionContextHolder.getCurrentTransactionContext();

TransactionContextHolder ist nur ein statischer Wrapper umThreadLocal, derTransactionContext enthält.

4.2. Wer legt den Thread-lokalen Kontext fest?

Wenn wir uns ansehen, wer diesetCurrentTransactionContext-Methode aufruft, finden wir nur einen Aufrufer:TransactionalTestExecutionListener.beforeTestMethod.

TransactionalTestExecutionListener ist der Listener, den Springs bei Tests mit Anmerkungen@Transactional automatisch konfiguriert.

Beachten Sie, dassTransactionContext keinen Verweis auf eine tatsächliche Transaktion enthält. stattdessen ist es lediglich eine Fassade über denPlatformTransactionManager.

Ja, dieser Code ist stark geschichtet und abstrakt. Dies sind häufig die Kernbestandteile des Spring-Frameworks.

Es ist interessant zu sehen, wie Spring unter der Komplexität keine schwarze Magie ausübt - nur eine Menge notwendiger Buchhaltung, Sanitär, Ausnahmebehandlung und so weiter.

5. Schlussfolgerungen

In diesem kurzen Tutorial haben wir gesehen, wie Sie programmgesteuert mit Transaktionen in Spring-basierten Tests interagieren.

Die Implementierung all dieser Beispiele finden Sie inthe GitHub project - dies ist ein Maven-Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.