Spring Transaction Management Beispiel JDBC

Spring Transaction Management ist eine der am weitesten verbreiteten und wichtigsten Funktionen des Spring-Frameworks. Transaktionsmanagement ist eine triviale Aufgabe in jeder Unternehmensanwendung. Wir haben bereits gelernt, wie man die JDBC-API für das Transaktionsmanagement verwendet. Spring bietet umfangreiche Unterstützung für das Transaktionsmanagement und hilft Entwicklern, sich mehr auf die Geschäftslogik zu konzentrieren, anstatt sich um die Integrität der Daten im Falle von Systemfehlern zu sorgen.

Spring Transaktionsmanagement

Einige der Vorteile der Verwendung von Spring Transaction Management sind:

  1. Unterstützung für deklaratives Transaktionsmanagement. In diesem Modell verwendet Spring AOP über die transaktionalen Methoden, um die Datenintegrität bereitzustellen. Dies ist der bevorzugte Ansatz und funktioniert in den meisten Fällen.
  2. Unterstützung für die meisten Transaktions-APIs wie JDBC, Hibernate, JPA, JDO, JTA usw. Alles, was wir tun müssen, ist die richtige Implementierungsklasse für den Transaktionsmanager zu verwenden. Zum Beispiel org.springframework.jdbc.datasource.DriverManagerDataSource für das JDBC-Transaktionsmanagement und org.springframework.orm.hibernate3.HibernateTransactionManager, wenn wir Hibernate als ORM-Tool verwenden.
  3. Unterstützung für programmgesteuertes Transaktionsmanagement durch Verwendung von TransactionTemplate oder Implementierung von PlatformTransactionManager.

Die meisten Funktionen, die wir in einem Transaktionsmanager möchten, werden durch deklaratives Transaktionsmanagement 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, bei dem wir mehrere Tabellen in einer einzigen Transaktion aktualisieren. Die Transaktion soll nur dann bestätigt werden, wenn alle JDBC-Anweisungen erfolgreich ausgeführt werden, andernfalls soll sie zurückgerollt werden, um Inkonsistenzen zu vermeiden. Wenn Sie die JDBC-Transaktionsverwaltung kennen, könnten Sie argumentieren, dass wir dies leicht erreichen können, indem wir die automatische Bestätigung für die Verbindung auf „false“ setzen und basierend auf dem Ergebnis aller Anweisungen entweder die Transaktion bestätigen oder zurückrollen. Natürlich können wir das tun, aber das würde zu einem großen Aufwand an Standardcode nur für die Transaktionsverwaltung führen. Außerdem wäre derselbe Code an allen Stellen vorhanden, an denen wir nach Transaktionsverwaltung suchen, was zu eng gekoppeltem und nicht wartbarem Code führt. Das deklarative Transaktionsmanagement von Spring behebt diese Probleme, indem es Aspektorientierte Programmierung verwendet, um eine lose Kopplung zu erreichen und Standardcode in unserer Anwendung zu vermeiden. Schauen wir uns anhand eines einfachen Beispiels an, wie Spring das macht. Bevor wir in unser Spring-Projekt einsteigen, richten wir unsere Datenbank für unseren Gebrauch ein.

Spring Transaktionsmanagement – Datenbanksetup

Wir werden zwei Tabellen für unseren Gebrauch erstellen und beide in einer einzigen Transaktion aktualisieren.

CREATE TABLE `Customer` (
  `id` int(11) unsigned NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Address` (
  `id` int(11) unsigned NOT NULL,
  `address` varchar(20) DEFAULT NULL,
  `country` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Wir könnten hier eine Fremdschlüsselbeziehung von der Spalte „Address id“ zur Spalte „Customer id“ definieren, aber der Einfachheit halber habe ich hier keine Einschränkung definiert. Unsere Datenbankkonfiguration ist bereit für das Spring-Transaktionsmanagement-Projekt. Lassen Sie uns ein einfaches Spring Maven-Projekt in der Spring Tool Suite erstellen. Unsere finale Projektstruktur wird wie das untenstehende Bild aussehen. Schauen wir uns jeden Teil einzeln an; gemeinsam werden sie ein einfaches Beispiel für das Spring-Transaktionsmanagement mit JDBC bieten.

Spring Transaktionsmanagement – Maven-Abhängigkeiten

Da wir die JDBC-API verwenden, müssen wir die spring-jdbc-Abhängigkeit in unsere Anwendung aufnehmen. Wir benötigen auch den MySQL-Datenbanktreiber, um eine Verbindung zur MySQL-Datenbank herzustellen. Daher werden wir auch die mysql-connector-java-Abhängigkeit einschließen. Das spring-tx-Artefakt stellt Abhängigkeiten für das Transaktionsmanagement bereit. Normalerweise wird es automatisch von STS eingefügt, aber wenn nicht, müssen Sie es ebenfalls hinzufügen. Sie könnten einige andere Abhängigkeiten für Logging und Unittests sehen, jedoch werden wir keine davon verwenden. Unsere finale pom.xml-Datei sieht wie der unten stehende Code aus.

<project 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>

		<!-- Generic properties -->
		<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 and Transactions -->
		<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 and MySQL Driver -->
		<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 with 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 Artifacts -->
		<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>
</project>

I have updated the Spring versions to the latest one as of today. Make sure MySQL database driver is compatible with your mysql installation.

Spring-Transaktionsmanagement – Modellklassen

Wir werden zwei Java-Beans erstellen, Customer und Address, die unseren Tabellen zugeordnet werden.

package com.journaldev.spring.jdbc.model;

public class Address {

	private int id;
	private String address;
	private String country;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getCountry() {
		return country;
	}
	public void setCountry(String country) {
		this.country = country;
	}
	
}
package com.journaldev.spring.jdbc.model;

public class Customer {

	private int id;
	private String name;
	private Address address;
	
	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 Address getAddress() {
		return address;
	}
	public void setAddress(Address address) {
		this.address = address;
	}
	
}

Beachten Sie, dass die Customer-Bean Address als eine ihrer Variablen hat. Wenn wir den DAO für Customer implementieren, erhalten wir Daten für sowohl die Kunden- als auch die Adresstabelle, und wir führen zwei separate Einfügeabfragen für diese Tabellen aus. Deshalb benötigen wir ein Transaktionsmanagement, um Dateninkonsistenzen zu vermeiden.

Spring-Transaktionsmanagement – DAO-Implementierung

Lassen Sie uns den DAO für die Customer-Bean implementieren. Zur Vereinfachung haben wir nur eine Methode zum Einfügen von Datensätzen in beide Tabellen.

package com.journaldev.spring.jdbc.dao;

import com.journaldev.spring.jdbc.model.Customer;

public interface CustomerDAO {

	public void create(Customer customer);
}
package com.journaldev.spring.jdbc.dao;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

import com.journaldev.spring.jdbc.model.Customer;

public class CustomerDAOImpl implements CustomerDAO {

	private DataSource dataSource;

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	@Override
	public void create(Customer customer) {
		String queryCustomer = "insert into Customer (id, name) values (?,?)";
		String queryAddress = "insert into Address (id, address,country) values (?,?,?)";

		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

		jdbcTemplate.update(queryCustomer, new Object[] { customer.getId(),
				customer.getName() });
		System.out.println("Inserted into Customer Table Successfully");
		jdbcTemplate.update(queryAddress, new Object[] { customer.getId(),
				customer.getAddress().getAddress(),
				customer.getAddress().getCountry() });
		System.out.println("Inserted into Address Table Successfully");
	}

}

Beachten Sie, dass die CustomerDAO-Implementierung sich nicht um das Transaktionsmanagement kümmert. Auf diese Weise erreichen wir eine Trennung der Anliegen, weil wir manchmal DAO-Implementierungen von Dritten erhalten und wir keine Kontrolle über diese Klassen haben.

Frühlingsdeklarative Transaktionsverwaltung – Service

Erstellen wir einen Kundenservice, der die CustomerDAO-Implementierung verwendet und Transaktionsverwaltung bereitstellt, wenn Datensätze in den Kunden- und Adresstabellen in einer einzigen Methode eingefügt werden.

package com.journaldev.spring.jdbc.service;

import com.journaldev.spring.jdbc.model.Customer;

public interface CustomerManager {

	public void createCustomer(Customer cust);
}
package com.journaldev.spring.jdbc.service;

import org.springframework.transaction.annotation.Transactional;

import com.journaldev.spring.jdbc.dao.CustomerDAO;
import com.journaldev.spring.jdbc.model.Customer;

public class CustomerManagerImpl implements CustomerManager {

	private CustomerDAO customerDAO;

	public void setCustomerDAO(CustomerDAO customerDAO) {
		this.customerDAO = customerDAO;
	}

	@Override
	@Transactional
	public void createCustomer(Customer cust) {
		customerDAO.create(cust);
	}

}

Wenn Sie die CustomerManager-Implementierung bemerken, verwendet sie nur die CustomerDAO-Implementierung, um den Kunden zu erstellen, bietet jedoch deklarative Transaktionsverwaltung durch Annotation der createCustomer()-Methode mit @Transactional-Annotation. Das ist alles, was wir in unserem Code tun müssen, um die Vorteile der Spring-Transaktionsverwaltung zu nutzen. @Transactional-Annotation kann sowohl auf Methoden als auch auf ganze Klassen angewendet werden. Wenn Sie möchten, dass alle Ihre Methoden Transaktionsverwaltungsfunktionen haben, sollten Sie Ihre Klasse mit dieser Annotation versehen. Lesen Sie mehr über Annotationen unter Java-Annotations-Tutorial. Der einzige verbleibende Teil besteht darin, Spring-Beans zu verdrahten, um das Beispiel für die Spring-Transaktionsverwaltung zum Laufen zu bringen.

Spring Transaktionsverwaltung – Bean-Konfiguration

Erstellen Sie eine Spring Bean-Konfigurationsdatei mit dem Namen „spring.xml“. Wir werden dies in unserem Testprogramm verwenden, um Spring Beans zu verknüpfen und unser JDBC-Programm zur Testung des Transaktionsmanagements auszuführen.

<?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">

	<!-- Enable Annotation based Declarative Transaction Management -->
	<tx:annotation-driven proxy-target-class="true"
		transaction-manager="transactionManager" />

	<!-- Creating TransactionManager Bean, since JDBC we are creating of type 
		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="customerDAO" class="com.journaldev.spring.jdbc.dao.CustomerDAOImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<bean id="customerManager" class="com.journaldev.spring.jdbc.service.CustomerManagerImpl">
		<property name="customerDAO" ref="customerDAO"></property>
	</bean>

</beans>

Wichtige Punkte, die in der Spring Bean-Konfigurationsdatei beachtet werden sollten, sind:

  • Das Element „tx:annotation-driven“ wird verwendet, um dem Spring-Kontext mitzuteilen, dass wir eine transaktionsbasierte Konfiguration mit Annotationen verwenden. Das Attribut „transaction-manager“ wird verwendet, um den Namen des Transaktionsmanagers bereitzustellen. Der Standardwert für „transaction-manager“ ist „transactionManager“, aber ich habe ihn trotzdem angegeben, um Verwirrungen zu vermeiden. Das Attribut „proxy-target-class“ wird verwendet, um dem Spring-Kontext mitzuteilen, dass Klassenbasierte Proxies verwendet werden sollen. Ohne dieses Attribut erhalten Sie zur Laufzeit eine Ausnahme mit einer Meldung wie „Exception in thread ‚main‘ org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean mit dem Namen ‚customerManager‘ muss vom Typ [com.journaldev.spring.jdbc.service.CustomerManagerImpl] sein, war aber tatsächlich vom Typ [com.sun.proxy.$Proxy6]“.
  • Da wir JDBC verwenden, erstellen wir den „transactionManager“-Bean vom Typ „org.springframework.jdbc.datasource.DataSourceTransactionManager“. Dies ist sehr wichtig und wir sollten die richtige Implementierungsklasse des Transaktionsmanagers basierend auf unserer Transaktions-API verwenden.
  • Der „dataSource“-Bean wird verwendet, um das DataSource-Objekt zu erstellen, und wir müssen die Datenbankkonfigurationseigenschaften wie „driverClassName“, „url“, „username“ und „password“ angeben. Ändern Sie diese Werte entsprechend Ihren lokalen Einstellungen.
  • Wir injizieren dataSource in das customerDAO-Bean. Ebenso injizieren wir das customerDAO-Bean in die Definition des customerManager-Beans.

Unsere Einrichtung ist bereit, erstellen wir eine einfache Testklasse, um unsere Transaktionsverwaltungsimplementierung zu testen.

package com.journaldev.spring.jdbc.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.journaldev.spring.jdbc.model.Address;
import com.journaldev.spring.jdbc.model.Customer;
import com.journaldev.spring.jdbc.service.CustomerManager;
import com.journaldev.spring.jdbc.service.CustomerManagerImpl;

public class TransactionManagerMain {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
				"spring.xml");

		CustomerManager customerManager = ctx.getBean("customerManager",
				CustomerManagerImpl.class);

		Customer cust = createDummyCustomer();
		customerManager.createCustomer(cust);

		ctx.close();
	}

	private static Customer createDummyCustomer() {
		Customer customer = new Customer();
		customer.setId(2);
		customer.setName("Pankaj");
		Address address = new Address();
		address.setId(2);
		address.setCountry("India");
		// Wert auf mehr als 20 Zeichen setzen, damit eine SQLException auftritt
		address.setAddress("Albany Dr, San Jose, CA 95129");
		customer.setAddress(address);
		return customer;
	}

}

Beachten Sie, dass ich den Wert der Adressenspalte explizit zu lang setze, damit beim Einfügen von Daten in die Adresstabelle eine Ausnahme auftritt. Wenn wir nun unser Testprogramm ausführen, erhalten wir folgende Ausgabe.

Mar 29, 2014 7:59:32 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3fa99295: startup date [Sat Mar 29 19:59:32 PDT 2014]; root of context hierarchy
Mar 29, 2014 7:59:32 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Mar 29, 2014 7:59:32 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Loaded JDBC driver: com.mysql.jdbc.Driver
Inserted into Customer Table Successfully
Mar 29, 2014 7:59:32 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
Mar 29, 2014 7:59:32 PM org.springframework.jdbc.support.SQLErrorCodesFactory <init>
INFO: SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]
Exception in thread "main" org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into Address (id, address,country) values (?,?,?)]; Data truncation: Data too long for column 'address' at row 1; nested exception is com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'address' at row 1
	at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:100)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:658)
	at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:907)
	at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:968)
	at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:978)
	at com.journaldev.spring.jdbc.dao.CustomerDAOImpl.create(CustomerDAOImpl.java:27)
	at com.journaldev.spring.jdbc.service.CustomerManagerImpl.createCustomer(CustomerManagerImpl.java:19)
	at com.journaldev.spring.jdbc.service.CustomerManagerImpl$$FastClassBySpringCGLIB$$84f71441.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
	at com.journaldev.spring.jdbc.service.CustomerManagerImpl$$EnhancerBySpringCGLIB$$891ec7ac.createCustomer(<generated>)
	at com.journaldev.spring.jdbc.main.TransactionManagerMain.main(TransactionManagerMain.java:20)
Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'address' at row 1
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2939)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1623)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1715)
	at com.mysql.jdbc.Connection.execSQL(Connection.java:3249)
	at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1268)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1541)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1455)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1440)
	at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:914)
	at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:907)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:642)
	... 16 more

Beachten Sie, dass die Protokollmeldung besagt, dass die Daten erfolgreich in die Kunden-Tabelle eingefügt wurden, aber die Ausnahme, die vom MySQL-Datenbanktreiber ausgelöst wurde, deutlich sagt, dass der Wert für die Adressspalte zu lang ist. Wenn Sie jetzt die Kunden-Tabelle überprüfen, werden Sie dort keine Zeile finden, was bedeutet, dass die Transaktion vollständig zurückgerollt wurde. Wenn Sie sich fragen, wo die Transaktionsverwaltungsmagie 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 CustomerManagerImpl zu generieren, und bestätigt die Transaktion nur, wenn die Methode erfolgreich zurückkehrt. Wenn es eine Ausnahme gibt, wird einfach die gesamte Transaktion zurückgerollt. Ich würde Ihnen empfehlen, Spring AOP Beispiel zu lesen, um mehr über das Modell der aspektorientierten Programmierung zu erfahren. Das ist alles zum Beispiel für die Transaktionsverwaltung in Spring. Laden Sie das Beispielsprojekt über den unten stehenden Link herunter und probieren Sie es aus, um mehr zu lernen.

Spring JDBC Transaktionsverwaltungsprojekt herunterladen

Source:
https://www.digitalocean.com/community/tutorials/spring-transaction-management-jdbc-example