Dieser Artikel beschreibt das Transaktionsmanagement in Spring anhand von Beispielen. Teilen Sie es als Referenz mit allen. Die spezifische Analyse lautet wie folgt:
Geschäftseinführung:
Das Transaktionsmanagement ist eine wesentliche Technologie bei der Entwicklung von Unternehmensanwendungen, um Datenintegrität und -konsistenz sicherzustellen
Eine Transaktion ist eine Reihe von Aktionen, die als eine einzelne Arbeitseinheit behandelt werden. Entweder funktionieren alle diese Aktionen oder keine davon
Vier Schlüsselattribute von Transaktionen (ACID)
① Atomarität: Eine atomare Operation im Transaktionsraum besteht aus einer Reihe von Aktionen. Die Atomizität von Transaktionen stellt sicher, dass Aktionen entweder vollständig abgeschlossen sind oder überhaupt keine Wirkung haben. ② Konsistenz: Sobald alle Transaktionsaktionen abgeschlossen sind, wird die Transaktion festgeschrieben. Daten und Ressourcen befinden sich in einem konsistenten Zustand, der den Geschäftsregeln entspricht. ③ Isolation: Es kann sein, dass viele Transaktionen gleichzeitig dieselben Daten verarbeiten. Daher sollte alles von anderen Transaktionen isoliert werden, um Datenbeschädigung zu verhindern. ④ Haltbarkeit: Sobald eine Transaktion abgeschlossen ist , sollten die Ergebnisse unabhängig von auftretenden Systemfehlern nicht beeinträchtigt werden. Typischerweise werden die Ergebnisse einer Transaktion in den persistenten Speicher geschrieben
Transaktionsmanagement im Frühjahr
Als Framework für Unternehmensanwendungen definiert Spring eine Abstraktionsschicht über verschiedenen Transaktionsverwaltungs-APIs. Anwendungsentwickler müssen die zugrunde liegende Transaktionsverwaltungs-API nicht verstehen, um den Transaktionsverwaltungsmechanismus von Spring verwenden zu können.
Spring unterstützt sowohl programmatisches Transaktionsmanagement als auch deklaratives Transaktionsmanagement.
Programmatische Transaktionsverwaltung: Betten Sie Transaktionsverwaltungscode in Geschäftsmethoden ein, um die Übermittlung und das Rollback von Transaktionen zu steuern. Bei programmgesteuerten Transaktionen muss in jeden Geschäftsvorgang zusätzlicher Transaktionsverwaltungscode einbezogen werden.
Deklaratives Transaktionsmanagement: In den meisten Fällen besser zu verwenden als programmatisches Transaktionsmanagement. Es trennt den Transaktionsverwaltungscode von den Geschäftsmethoden und implementiert die Transaktionsverwaltung auf deklarative Weise. Das Transaktionsmanagement als Querschnittsthema kann durch AOP-Methoden modularisiert werden. Spring unterstützt die deklarative Transaktionsverwaltung durch das Spring AOP-Framework.
Ausbreitungseigenschaften von Spring-Transaktionen:
Wenn eine Transaktionsmethode von einer anderen Transaktionsmethode aufgerufen wird, müssen Sie angeben, wie die Transaktion weitergegeben werden soll. Beispielsweise kann die Methode weiterhin in der vorhandenen Transaktion ausgeführt werden oder eine neue Transaktion starten und in einer eigenen Transaktion ausgeführt werden.
Das Weitergabeverhalten einer Transaktion kann durch das Weitergabeattribut angegeben werden. Spring definiert 7 Kommunikationsverhalten:
Kommunikationsverhalten | Bedeutung |
PROPAGATION_MANDATORY | Gibt an, dass die Methode innerhalb einer Transaktion ausgeführt werden muss. Wenn die aktuelle Transaktion nicht vorhanden ist, wird eine Ausnahme ausgelöst. |
PROPAGATION_NESTED | Gibt an, dass diese Methode in einer verschachtelten Transaktion ausgeführt wird, wenn derzeit eine Transaktion vorhanden ist. Verschachtelte Transaktionen können unabhängig von der aktuellen Transaktion festgeschrieben oder zurückgesetzt werden. Wenn die aktuelle Transaktion nicht vorhanden ist, ist das Verhalten dasselbe wie bei PROPAGATION_REQUIRED. Beachten Sie, dass die Unterstützung dieses Ausbreitungsverhaltens von Hersteller zu Hersteller unterschiedlich ist. Sie können in der Dokumentation Ihrer Ressourcenmanager nachsehen, ob diese verschachtelte Transaktionen unterstützen. |
PROPAGATION_NEVER | Gibt an, dass die aktuelle Methode nicht in einem Transaktionskontext ausgeführt werden soll. Wenn gerade eine Transaktion läuft, wird eine Ausnahme ausgelöst |
PROPAGATION_NOT_SUPPORTED | Gibt an, dass diese Methode nicht innerhalb einer Transaktion ausgeführt werden soll. Wenn eine aktuelle Transaktion vorhanden ist, wird diese angehalten, während diese Methode ausgeführt wird. Wenn Sie JTATransactionManager verwenden, müssen Sie auf TransactionManager zugreifen |
PROPAGATION_REQUIRED | Gibt an, dass die aktuelle Methode innerhalb einer Transaktion ausgeführt werden muss. Wenn die aktuelle Transaktion vorhanden ist, wird die Methode in dieser Transaktion ausgeführt. Andernfalls wird eine neue Transaktion gestartet |
PROPAGATION_REQUIRED_NEW | Gibt an, dass die aktuelle Methode in einer eigenen Transaktion ausgeführt werden muss. Eine neue Transaktion wird gestartet. Wenn eine aktuelle Transaktion vorhanden ist, wird diese während der Ausführung dieser Methode angehalten. Wenn Sie JTATransactionManager verwenden, müssen Sie auf TransactionManager zugreifen |
PROPAGATION_SUPPORTS | Zeigt an, dass die aktuelle Methode keinen Transaktionskontext erfordert. Wenn jedoch eine aktuelle Transaktion vorhanden ist, wird die Methode in dieser Transaktion ausgeführt |
Unter ihnen ist PROPAGATION_REQUIRED das Standardpropagierungsattribut
Probleme, die durch gleichzeitige Transaktionen verursacht werden
Viele unerwartete Probleme können auftreten, wenn mehrere Transaktionen in derselben Anwendung oder in verschiedenen Anwendungen gleichzeitig für denselben Datensatz ausgeführt werden.
Durch gleichzeitige Transaktionen verursachte Probleme können in die folgenden drei Kategorien unterteilt werden:
① Dirty Read: Dirty Read tritt auf, wenn eine Transaktion Daten liest, die von einer anderen Transaktion neu geschrieben, aber noch nicht festgeschrieben wurden. Wenn das Umschreiben später zurückgesetzt wird, sind die durch die erste Transaktion erhaltenen Daten ungültig.
② Nicht wiederholbares Lesen: Nicht wiederholbares Lesen tritt auf, wenn eine Transaktion dieselbe Abfrage zweimal oder öfter ausführt, aber jedes Mal unterschiedliche Daten erhält. Dies liegt normalerweise daran, dass eine andere gleichzeitige Transaktion die Daten zwischen Abfragen aktualisiert hat
③ Phantomlesen: Phantomlesen ähnelt dem nicht wiederholbaren Lesen. Dies geschieht, wenn eine Transaktion (T1) einige Datenzeilen liest und dann eine andere gleichzeitige Transaktion (T2) einige Daten einfügt. Bei nachfolgenden Abfragen stellt die erste Transaktion (T1) fest, dass es weitere Datensätze gibt, die ursprünglich nicht vorhanden waren.
Codebeispiel
Zuerst ist die Datenbanktabelle:
Einschließlich Buch (ISBN, Buchname, Preis), Konto (Benutzername, Kontostand), Buchbestand (ISBN, Lagerbestand)
Dann werden die Klassen verwendet:
BookShopDao
Kopieren Sie den Codecode wie folgt:
Paket com.yl.spring.tx;
öffentliche Schnittstelle BookShopDao {
//Ermitteln Sie den Stückpreis des Buches basierend auf der Buchnummer
public int findBookPriceByIsbn(String isbn);
//Aktualisieren Sie den Bestand des Buches, sodass der der Buchnummer entsprechende Bestand -1 ist
public void updateBookStock(String isbn);
//Kontostand des Benutzers aktualisieren: Saldopreis des Benutzernamens festlegen
public void updateUserAccount(String username, int price);
}
BookShopDaoImpl
Kopieren Sie den Codecode wie folgt:
Paket com.yl.spring.tx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository("bookShopDao")
Die öffentliche Klasse BookShopDaoImpl implementiert BookShopDao {
@Autowired
private JdbcTemplate JdbcTemplate;
@Override
public int findBookPriceByIsbn(String isbn) {
String sql = "SELECT price FROM book WHERE isbn = ?";
return JdbcTemplate.queryForObject(sql, Integer.class, isbn);
}
@Override
public void updateBookStock(String isbn) {
//Überprüfen Sie, ob der Buchbestand ausreicht. Wenn nicht, lösen Sie eine Ausnahme aus
String sql2 = "SELECT stock FROM book_stock WHERE isbn = ?";
int stock = JdbcTemplate.queryForObject(sql2, Integer.class, isbn);
if (stock == 0) {
throw new BookStockException("Unzureichender Lagerbestand!");
}
String sql = "UPDATE book_stock SET stock = stock - 1 WHERE isbn = ?";
JdbcTemplate.update(sql, isbn);
}
@Override
public void updateUserAccount(String username, int price) {
//Überprüfen Sie, ob das Guthaben nicht ausreicht. Wenn nicht, lösen Sie eine Ausnahme aus
String sql2 = „SELECT balance FROM account WHERE username = ?“;
int balance = JdbcTemplate.queryForObject(sql2, Integer.class, username);
if (Saldo < Preis) {
throw new UserAccountException("Unzureichendes Guthaben!");
}
String sql = "Konto aktualisieren SET Kontostand = Kontostand - ? WHERE Benutzername = ?";
JdbcTemplate.update(SQL, Preis, Benutzername);
}
}
BookShopService
Kopieren Sie den Code wie folgt: package com.yl.spring.tx;
öffentliche Schnittstelle BookShopService {
public void Purchase(String username, String isbn);
}
BookShopServiceImpl
Kopieren Sie den Codecode wie folgt:
Paket com.yl.spring.tx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service("bookShopService")
Die öffentliche Klasse BookShopServiceImpl implementiert BookShopService {
@Autowired
privater BookShopDao bookShopDao;
/**
* 1. Transaktionsanmerkungen hinzufügen
* Verwenden Sie die Weitergabe, um das Weitergabeverhalten der Transaktion anzugeben, dh wie die Transaktion verwendet wird, wenn die aktuelle Transaktionsmethode von einer anderen Transaktionsmethode aufgerufen wird.
* Der Standardwert ist ERFORDERLICH, was bedeutet, dass die Transaktion verwendet wird, die die Methode aufruft
* REQUIRES_NEW: Verwenden Sie Ihre eigene Transaktion und die Transaktion der aufgerufenen Transaktionsmethode wird angehalten.
*
* 2. Verwenden Sie Isolation, um die Isolationsstufe der Transaktion anzugeben. Der am häufigsten verwendete Wert ist READ_COMMITTED.
* 3. Standardmäßig setzt die deklarative Transaktion von Spring alle Laufzeitausnahmen zurück, die auch über die entsprechenden Eigenschaften festgelegt werden können. Normalerweise reicht der Standardwert aus.
* 4. Verwenden Sie readOnly, um anzugeben, ob die Transaktion schreibgeschützt ist. Zeigt an, dass diese Transaktion nur Daten liest, aber keine Daten aktualisiert, was dem Datenbankmodul bei der Optimierung der Transaktion helfen kann. Wenn es sich wirklich um eine Methode handelt, die nur die Datenbank liest, sollte readOnly=true gesetzt werden.
* 5. Verwenden Sie timeOut, um die Zeit anzugeben, die eine Transaktion dauern kann, bevor ein Rollback erzwungen wird.
*/
@Transactional(propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_COMMITTED,
noRollbackFor={UserAccountException.class},
readOnly=true, timeout=3)
@Override
public void Purchase(String username, String isbn) {
//1. Ermitteln Sie den Stückpreis des Buches
int preis = bookShopDao.findBookPriceByIsbn(isbn);
//2. Buchbestand aktualisieren
bookShopDao.updateBookStock(isbn);
//3. Benutzerguthaben aktualisieren
bookShopDao.updateUserAccount(Benutzername, Preis);;
}
}
BookStockException
Kopieren Sie den Codecode wie folgt:
Paket com.yl.spring.tx;
öffentliche Klasse BookStockException erweitert RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
public BookStockException() {
super();
// TODO Automatisch generierter Konstruktor-Stub
}
public BookStockException(String arg0, Throwable arg1, boolean arg2,
boolescher Wert3) {
super(arg0, arg1, arg2, arg3);
// TODO Automatisch generierter Konstruktor-Stub
}
public BookStockException(String arg0, Throwable arg1) {
super(arg0, arg1);
// TODO Automatisch generierter Konstruktor-Stub
}
public BookStockException(String arg0) {
super(arg0);
// TODO Automatisch generierter Konstruktor-Stub
}
public BookStockException(Throwable arg0) {
super(arg0);
// TODO Automatisch generierter Konstruktor-Stub
}
}
UserAccountException
Kopieren Sie den Codecode wie folgt:
Paket com.yl.spring.tx;
öffentliche Klasse UserAccountException erweitert RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
public UserAccountException() {
super();
// TODO Automatisch generierter Konstruktor-Stub
}
public UserAccountException(String arg0, Throwable arg1, boolean arg2,
boolescher Wert3) {
super(arg0, arg1, arg2, arg3);
// TODO Automatisch generierter Konstruktor-Stub
}
public UserAccountException(String arg0, Throwable arg1) {
super(arg0, arg1);
// TODO Automatisch generierter Konstruktor-Stub
}
public UserAccountException(String arg0) {
super(arg0);
// TODO Automatisch generierter Konstruktor-Stub
}
public UserAccountException(Throwable arg0) {
super(arg0);
// TODO Automatisch generierter Konstruktor-Stub
}
}
Kassierer
Kopieren Sie den Codecode wie folgt:
Paket com.yl.spring.tx;
java.util.List importieren;
öffentliche Schnittstelle Kassierer {
public void checkout(String username, List<String>isbns);
}
KassiererImpl. CashierImpl.checkout und bookShopService.purchase testen gemeinsam das Transaktionsweitergabeverhalten
Kopieren Sie den Codecode wie folgt:
Paket com.yl.spring.tx;
java.util.List importieren;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("Kassierer")
öffentliche Klasse CashierImpl implementiert Cashier {
@Autowired
privater BookShopService bookShopService;
@Transactional
@Override
public void checkout(String username, List<String> isbns) {
for(String isbn : isbns) {
bookShopService.purchase(Benutzername, isbn);
}
}
}
Testklasse:
Kopieren Sie den Codecode wie folgt:
Paket com.yl.spring.tx;
java.util.Arrays importieren;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
öffentliche Klasse SpringTransitionTest {
privater ApplicationContext ctx = null;
private BookShopDao bookShopDao = null;
privater BookShopService bookShopService = null;
privater Kassierer Kassierer = null;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
bookShopDao = ctx.getBean(BookShopDao.class);
bookShopService = ctx.getBean(BookShopService.class);
cashier = ctx.getBean(Cashier.class);
}
@Prüfen
public void testBookShopDaoFindPriceByIsbn() {
System.out.println(bookShopDao.findBookPriceByIsbn("1001"));
}
@Prüfen
public void testBookShopDaoUpdateBookStock(){
bookShopDao.updateBookStock("1001");
}
@Prüfen
public void testBookShopDaoUpdateUserAccount(){
bookShopDao.updateUserAccount("AA", 100);
}
@Prüfen
public void testBookShopService(){
bookShopService.purchase("AA", "1001");
}
@Prüfen
public void testTransactionPropagation(){
cashier.checkout("AA", Arrays.asList("1001", "1002"));
}
}
Ich hoffe, dass dieser Artikel für das Spring-Programmierungsdesign aller hilfreich sein wird.