Übersicht über das Spring Transaction Management
Spring Transaction Management ist eines der am häufigsten verwendeten und wichtigsten Merkmale des Spring-Frameworks. Die Transaktionsverwaltung ist eine triviale Aufgabe in jeder Unternehmensanwendung. Spring bietet umfangreiche Unterstützung für die Transaktionsverwaltung und hilft Entwicklern, sich mehr auf die Geschäftslogik zu konzentrieren, anstatt sich um die Integrität der Daten im Falle von Systemausfällen zu sorgen.
Vorteile der Verwendung von Spring Transaction Management
- Unterstützung für deklarative Transaktionsverwaltung. In diesem Modell verwendet Spring AOP über den transaktionalen Methoden, um die Datenintegrität zu gewährleisten. Dies ist der bevorzugte Ansatz und funktioniert in den meisten Fällen.
- Unterstützung für die meisten Transaktions-APIs wie JDBC, Hibernate, JPA, JDO, JTA usw. Alles, was wir tun müssen, ist, die richtige Transaktionsmanager-Implementierungsklasse zu verwenden. Zum Beispiel org.springframework.jdbc.datasource.DriverManagerDataSource für JDBC-Transaktionsmanagement und org.springframework.orm.hibernate3.HibernateTransactionManager, wenn wir Hibernate als ORM-Tool verwenden.
- Unterstützung für programmatische Transaktionsverwaltung durch Verwendung von TransactionTemplate oder PlatformTransactionManager-Implementierung.
- Die meisten der Funktionen, die wir von einem Transaktionsmanager erwarten würden, werden von der deklarativen Transaktionsverwaltung unterstützt, daher würden wir diesen Ansatz für unser Beispielprojekt verwenden.
Spring Transaction Management JDBC-Beispiel
Wir werden ein einfaches Spring JDBC-Projekt erstellen, in dem wir mehrere Tabellen in einer einzigen Transaktion aktualisieren. Die Transaktion sollte nur dann durchgeführt werden, wenn alle JDBC-Anweisungen erfolgreich ausgeführt werden, andernfalls sollte sie zum Vermeiden von Dateninkonsistenzen zurückgesetzt werden.
Wenn Sie die JDBC-Transaktionsverwaltung kennen, könnten Sie argumentieren, dass wir dies einfach erreichen können, indem wir das Auto-Commit für die Verbindung auf falsch setzen und basierend auf dem Ergebnis aller Anweisungen entweder die Transaktion bestätigen oder zurücksetzen. Natürlich können wir das tun, aber das würde zu viel Boiler-Plate-Code nur für die Transaktionsverwaltung führen. Außerdem wäre derselbe Code überall vorhanden, wo wir nach Transaktionsverwaltung suchen, was zu eng gekoppeltem und nicht wartbarem Code führt. Die deklarative Transaktionsverwaltung von Spring adressiert diese Bedenken, indem sie aspektorientierte Programmierung verwendet, um lose Kopplung zu erreichen und Boiler-Plate-Code in unserer Anwendung zu vermeiden. Lassen Sie uns sehen, wie Spring dies mit einem einfachen Beispiel macht. Bevor wir in unser Spring-Projekt einsteigen, lassen Sie uns einige Datenbankeinstellungen für unseren Gebrauch vornehmen.
Spring Transaction Management – Datenbankeinrichtung
Wir werden zwei Tabellen für unseren Gebrauch erstellen und beide in einer einzigen Transaktion aktualisieren.
CREATE TABLE `Kunde` (
`id` int(11) unsigned NOT NULL,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Adresse` (
`id` int(11) unsigned NOT NULL,
`adresse` varchar(20) DEFAULT NULL,
`land` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Wir könnten hier eine Fremdschlüsselbeziehung von der Adressen-ID-Spalte zur Kunden-ID-Spalte definieren, aber der Einfachheit halber haben wir hier keine Einschränkung definiert. Unsere Datenbankeinrichtung ist bereit für das Spring Transaction Management-Projekt, lassen Sie uns ein einfaches Spring Maven-Projekt im Spring Tool Suite erstellen.
Spring Transaction Management-Beispiel
Lassen Sie uns jedes der Teile einzeln betrachten, zusammen bieten sie ein einfaches Spring Transaction Management-Beispiel mit JDBC.
Spring Transaction Management – Maven-Abhängigkeiten
Da wir die JDBC-API verwenden, müssen wir die Abhängigkeit von spring-jdbc in unserer Anwendung einbeziehen. Wir benötigen auch den MySQL-Datenbanktreiber, um eine Verbindung zur MySQL-Datenbank herzustellen, also werden wir auch die Abhängigkeit von mysql-connector-java einbeziehen. Das Artefakt spring-tx bietet Abhängigkeiten für die Transaktionsverwaltung, normalerweise wird es automatisch von STS eingefügt, aber wenn nicht, müssen Sie es auch einfügen. Sie könnten einige andere Abhängigkeiten für das Logging und das Unit-Testing sehen, jedoch werden wir keine davon verwenden. Unsere endgültige pom.xml-Datei sieht wie der untenstehende Code aus.
<projekt xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>SpringJDBCTransactionManagement</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<!-- Generische Eigenschaften -->
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring -->
<spring-framework.version>4.0.2.RELEASE</spring-framework.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
<!-- Test -->
<junit.version>4.11</junit.version>
</properties>
<dependencies>
<!-- Spring und Transaktionen -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Spring JDBC und MySQL-Treiber -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.5</version>
</dependency>
<!-- Logging mit SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Test-Artefakte -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</projekt>
Wir haben die Spring-Versionen auf den neuesten Stand gebracht. Stellen Sie sicher, dass der MySQL-Datenbanktreiber mit Ihrer MySQL-Installation kompatibel ist.
Spring Transaction Management – Modellklassen
Wir werden zwei Java Beans, Kunde und Adresse, erstellen, die unseren Tabellen entsprechen.
package com.journaldev.spring.jdbc.model;
public class Adresse {
private int id;
private String adresse;
private String land;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAdresse() {
return adresse;
}
public void setAdresse(String adresse) {
this.adresse = adresse;
}
public String getLand() {
return land;
}
public void setLand(String land) {
this.land = land;
}
}
package com.journaldev.spring.jdbc.model;
public class Kunde {
private int id;
private String name;
private Adresse adresse;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Adresse getAdresse() {
return adresse;
}
public void setAdresse(Adresse adresse) {
this.adresse = adresse;
}
}
Beachten Sie, dass das Kunden-Bean eine Adresse als eine seiner Variablen hat. Wenn wir DAO für Kunden implementieren, erhalten wir Daten für beide Kundentabellen und Adresstabellen und wir werden zwei separate Einfügeabfragen für diese Tabellen ausführen, und deshalb benötigen wir Transaktionsmanagement, um Dateninkonsistenzen zu vermeiden.
Spring Transaction Management – DAO-Implementierung
Lassen Sie uns das DAO für das Kunden-Bean implementieren, für die Einfachheit werden wir nur eine Methode haben, um Datensätze in beiden Kundentabellen und Adresstabellen einzufügen.
package com.journaldev.spring.jdbc.dao;
public interface KundenDAO {
public void create(Kunde kunde);
}
package com.journaldev.spring.jdbc.dao;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import com.journaldev.spring.jdbc.model.Kunde;
public class KundenDAOImpl implements KundenDAO {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public void create(Kunde kunde) {
String queryKunde = "insert into Kunde (id, name) values (?,?)";
String queryAdresse = "insert into Adresse (id, adresse, land) values (?,?,?)";
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update(queryKunde, new Object[] { kunde.getId(),
kunde.getName() });
System.out.println("Erfolgreich in Kundentabelle eingefügt");
jdbcTemplate.update(queryAdresse, new Object[] { kunde.getId(),
kunde.getAdresse().getAdresse(),
kunde.getAdresse().getLand() });
System.out.println("Erfolgreich in Adresstabelle eingefügt");
}
}
Beachten Sie, dass die KundenDAO-Implementierung sich nicht um die Transaktionsverwaltung kümmert. Auf diese Weise erreichen wir eine Trennung der Anliegen, da wir manchmal DAO-Implementierungen von Drittanbietern erhalten und wir keinen Einfluss auf diese Klassen haben.
Spring Declarative Transaction Management – Service
Lassen Sie uns einen Kunden-Service erstellen, der die KundenDAO-Implementierung verwendet und Transaktionsmanagement bereitstellt, wenn Datensätze in den Kundentabellen und Adresstabellen in einer einzigen Methode eingefügt werden.
package com.journaldev.spring.jdbc.service;
public interface KundenManager {
public void createKunde(Kunde kunde);
}
package com.journaldev.spring.jdbc.service;
import org.springframework.transaction.annotation.Transactional;
import com.journaldev.spring.jdbc.dao.KundenDAO;
import com.journaldev.spring.jdbc.model.Kunde;
public class KundenManagerImpl implements KundenManager {
private KundenDAO kundenDAO;
public void setKundenDAO(KundenDAO kundenDAO) {
this.kundenDAO = kundenDAO;
}
@Override
@Transactional
public void createKunde(Kunde kunde) {
kundenDAO.create(kunde);
}
}
Wenn Sie sich die KundenManager-Implementierung ansehen, wird deutlich, dass sie lediglich die KundenDAO-Implementierung verwendet, um den Kunden zu erstellen, aber die deklarative Transaktionsverwaltung durch Annotieren der createKunde()-Methode mit der @Transactional-Anmerkung bereitstellt. Das ist alles, was wir in unserem Code tun müssen, um die Vorteile des Spring-Transaktionsmanagements zu nutzen.
Spring Transaction Management – Bean-Konfiguration
Erstellen Sie eine Spring Bean-Konfigurationsdatei mit dem Namen „spring.xml“. Wir werden diese in unserem Testprogramm verwenden, um Spring-Beans zu verdrahten und unser JDBC-Programm auszuführen, um das Transaktionsmanagement zu testen.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:context="https://www.springframework.org/schema/context"
xmlns:tx="https://www.springframework.org/schema/tx"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-4.0.xsd
https://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- Aktivierung der auf Anmerkungen basierenden deklarativen Transaktionsverwaltung -->
<tx:annotation-driven proxy-target-class="true"
transaction-manager="transactionManager" />
<!-- Erstellen des TransactionManager-Beans, da JDBC, erstellen wir von Typ
DataSourceTransactionManager -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- MySQL DB DataSource -->
<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/TestDB" />
<property name="username" value="pankaj" />
<property name="password" value="pankaj123" />
</bean>
<bean id="kundenDAO" class="com.journaldev.spring.jdbc.dao.KundenDAOImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="kundenManager" class="com.journaldev.spring.jdbc.service.KundenManagerImpl">
<property name="kundenDAO" ref="kundenDAO"></property>
</bean>
</beans>
Wichtige Punkte, die in der Spring-Bean-Konfigurationsdatei zu beachten sind:
- Das Element tx:annotation-driven wird verwendet, um dem Spring-Kontext mitzuteilen, dass wir eine auf Anmerkungen basierende Transaktionsverwaltungskonfiguration verwenden. Das Attribut transaction-manager wird verwendet, um den Namen des Transaktionsmanager-Beans bereitzustellen. Der Standardwert von transaction-manager ist transactionManager, aber wir haben ihn trotzdem, um Verwirrung zu vermeiden. Das Attribut proxy-target-class wird verwendet, um dem Spring-Kontext mitzuteilen, Klassen-basierte Proxies zu verwenden, ohne sie erhalten Sie zur Laufzeit eine Ausnahme mit der Meldung wie Exception in thread „main“ org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named ‚kundenManager‘ must be of type [com.journaldev.spring.jdbc.service.KundenManagerImpl], but was actually of type [com.sun.proxy.$Proxy6]
- Da wir JDBC verwenden, erstellen wir ein transactionManager-Bean vom Typ org.springframework.jdbc.datasource.DataSourceTransactionManager. Dies ist sehr wichtig, und wir sollten die richtige Transaktionsmanager-Implementierungsklasse basierend auf unserer Transaktions-API verwenden.
- Das Bean dataSource wird verwendet, um das DataSource-Objekt zu erstellen, und wir müssen die Datenbankkonfigurationseigenschaften wie driverClassName, url, Benutzername und Passwort angeben. Ändern Sie diese Werte basierend auf Ihren lokalen Einstellungen.
- Wir injizieren dataSource in das kundenDAO-Bean. Ähnlich injizieren wir das kundenDAO-Bean in die kundenManager-Bean-Definition.
Unsere Einrichtung ist bereit, lassen Sie uns eine einfache Testklasse erstellen, um unsere Transaktionsverwaltungsimplementierung zu testen.
Spring Transaction Management – Testing
Hier ist ein Beispiel einer Testklasse, um unsere Transaktionsverwaltungsimplementierung zu überprüfen.
package com.journaldev.spring.jdbc.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.journaldev.spring.jdbc.model.Adresse;
import com.journaldev.spring.jdbc.model.Kunde;
import com.journaldev.spring.jdbc.service.KundenManager;
import com.journaldev.spring.jdbc.service.KundenManagerImpl;
public class TransactionManagerMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring.xml");
KundenManager kundenManager = ctx.getBean("kundenManager",
KundenManagerImpl.class);
Kunde kunde = erstelleDummyKunde();
kundenManager.createKunde(kunde);
ctx.close();
}
private static Kunde erstelleDummyKunde() {
Kunde kunde = new Kunde();
kunde.setId(2);
kunde.setName("Pankaj");
Adresse adresse = new Adresse();
adresse.setId(2);
adresse.setLand("Indien");
// Wert länger als 20 Zeichen einstellen, damit SQLException auftritt
adresse.setAdresse("Albany Dr, San Jose, CA 95129");
kunde.setAdresse(adresse);
return kunde;
}
}
Beachten Sie, dass wir absichtlich den Wert der Adressenspalte zu lang einstellen, sodass wir eine Ausnahme erhalten, während wir Daten in die Adresstabelle einfügen. Wenn wir nun unser Testprogramm ausführen, erhalten wir die folgende Ausgabe.
Mar 29, 2014 7:59:32 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Aktualisierung org.springframework.context.support.ClassPathXmlApplicationContext@3fa99295: Startdatum [Sat Mar 29 19:59:32 PDT 2014]; Wurzel der Kontexthierarchie
Mar 29, 2014 7:59:32 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Laden von XML-Bean-Definitionen aus Klassenpfad-Ressource [spring.xml]
Mar 29, 2014 7:59:32 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Geladener JDBC-Treiber: com.mysql.jdbc.Driver
Erfolgreich in Kundentabelle eingefügt
Mar 29, 2014 7:59:32 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Laden von XML-Bean-Definitionen aus Klassenpfad-Ressource [org/springframework/jdbc/support/sql-error-codes.xml]
Mar 29, 2014 7:59:32 PM org.springframework.jdbc.support.SQLErrorCodesFactory
INFO: SQLErrorCodes geladen: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]
Ausnahme in Thread "main" org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into Adresse (id, adresse,land) values (?,?,?)]; Datenverkürzung: Daten zu lang für Spalte 'adresse' in Zeile 1; Verschachtelte Ausnahme ist com.mysql.jdbc.MysqlDataTruncation: Datenverkürzung: Daten zu lang für Spalte 'adresse' in Zeile 1
bei org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:100)
bei org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
bei org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
bei org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
bei org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:658)
bei org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:907)
bei org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:968)
bei org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:978)
bei com.journaldev.spring.jdbc.dao.KundenDAOImpl.create(KundenDAOImpl.java:27)
bei com.journaldev.spring.jdbc.service.KundenManagerImpl.createKunde(KundenManagerImpl.java:19)
bei com.journaldev.spring.jdbc.service.KundenManagerImpl$$FastClassBySpringCGLIB$$84f71441.invoke()
bei org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
bei org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)
bei org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
bei org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
bei org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
bei org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
bei org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
bei org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
bei com.journaldev.spring.jdbc.service.KundenManagerImpl$$EnhancerBySpringCGLIB$$891ec7ac.createKunde()
bei com.journaldev.spring.jdbc.main.TransactionManagerMain.main(TransactionManagerMain.java:20)
Verursacht durch: com.mysql.jdbc.MysqlDataTruncation: Datenverkürzung: Daten zu lang für Spalte 'adresse' in Zeile 1
bei com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2939)
bei com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1623)
bei com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1715)
bei com.mysql.jdbc.Connection.execSQL(Connection.java:3249)
bei com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1268)
bei com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1541)
bei com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1455)
bei com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1440)
bei org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:914)
bei org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:907)
bei org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:642)
... 16 weitere
Beachten Sie, dass die Protokollnachricht sagt, dass Daten erfolgreich in die Kundentabelle eingefügt wurden, aber die von MySQL-Datenbanktreiber geworfene Ausnahme sagt deutlich, dass der Wert zu lang für die Adressenspalte ist. Wenn Sie nun die Kundentabelle überprüfen, werden Sie dort keine Zeile finden, was bedeutet, dass die Transaktion vollständig zurückgesetzt wurde. Wenn Sie sich fragen, wo das Transaktionsmanagement-Magie passiert, schauen Sie sich die Protokolle sorgfältig an und beachten Sie die AOP- und Proxy-Klassen, die vom Spring-Framework erstellt wurden. Das Spring-Framework verwendet Around-Advice, um eine Proxy-Klasse für KundenManagerImpl zu generieren und die Transaktion nur dann zu bestätigen, wenn die Methode erfolgreich zurückkehrt. Bei einer Ausnahme wird die gesamte Transaktion einfach zurückgesetzt. Das war’s für die Anleitung für das Spring Transaction Management-Beispiel.