preface

Spring transaction management

Spring’s transaction management simplifies the traditional transaction management process and improves the development efficiency. But first, you need to understand Spring’s database programming.

Spring database programming

Database programming is the foundation of Internet programming. Spring framework provides JDBC template mode, namely jdbcTemplate, for developers. It can simplify many codes, but jdbcTemplate is not commonly used in practical applications. Here, I’ll just cover Spring’s jdbcTemplate development.

SpringJDBC configuration

In this section, Spring database programming mainly uses the core and DataSource packages of the SpringJDBC module. Core is the core package of JDBC, including the commonly used JdbcTemplate class, and DataSource is the tool class package for accessing the DataSource. If you want to use SpringJDBC to operate the database, you need to perform the following configuration:

<! - configuration data source - > < bean id = "dataSource" class = ". Org. Springframework. JDBC dataSource. DriverManagerDataSource "> <! <property name="driverClassName" value=" com.mysql.jdbc.driver "/> <! - the connection url - > < property name = "url" value = "JDBC: mysql: / / localhost: 3306 / spring? characterEncoding=utf-8"/> <! Property name="username" value="root"/> <property name="password" value="root"/> </bean> <! - configure a JDBC template - > < bean id = "jdbcTemplate" class = "org. Springframework. JDBC. Core. JdbcTemplate" > < property name = "dataSource" ref="dataSource"/> </bean>Copy the code

In the example above, the dataSource needs to be injected into the jdbcTemplate to configure the JDBC template, and the jdbcTemplate needs to be injected into the corresponding bean when the jdbcTemplate needs to be used in the data access layer (Dao).

@repository ("testDao") public class TestDaoImpl implements testDao {@autoWired implements private JdbcTemplate by type jdbcTemplate;Copy the code

A common method for JDBCTemplate

Public int update(String SQL,Object args[]) : This method can add, modify, or delete data from a table. With args[], the function returns the number of updated rows. The following is an example:

String insertSQL="insert into user values(NULL,? ,?) "; Onject param[] = {"chencheng","m"}; jdbcTemplate.update(insertSQL,param);Copy the code
Public List<T> query(String SQL,RowMapper<T> RowMapper,Object args[])Copy the code

This method enables query operations on data tables, and rowMapper maps result sets to user-defined classes (provided that the class has the same attribute name as the field name). The following is an example:

jdbcTemplate.query(sql,new BeanPropertyRowMapper<User>(User.class),param);
Copy the code

Concrete implementation steps

1. Create and edit the configuration file applicationContext.xml

<? The XML version = "1.0" encoding = "utf-8"? > <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <! <context:component-scan base-package="com.ch5"/> <! - configuration data source - > < bean id = "dataSource" class = ". Org. Springframework. JDBC dataSource. DriverManagerDataSource "> <! <property name="driverClassName" value=" com.mysql.jdbc.driver "/> <! - the connection url - > < property name = "url" value = "JDBC: mysql: / / localhost: 3306 / spring? characterEncoding=utf-8"/> <! Property name="username" value="root"/> <property name="password" value="root"/> </bean> <! - configure a JDBC template - > < bean id = "jdbcTemplate" class = "org. Springframework. JDBC. Core. JdbcTemplate" > < property name = "dataSource" ref="dataSource"/> </bean> </beans>Copy the code

2. Create an entity class that maps the database

package com.ch5; public class User { private Integer id; private String name; private double money; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; }}Copy the code

Create TestDao and TestDaoImpl for the database access layer

package com.ch5.dao;
import com.ch5.User;
import java.util.List;
public interface TestDao {
    public int update(String sql,Object[] param);
    public List<User> query(String sql,Object[] param);
}
Copy the code
package com.ch5.dao.Impl; import com.ch5.User; import com.ch5.dao.TestDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.util.List; @repository ("testDao") public class TestDaoImpl implements testDao {@autoWired implements private JdbcTemplate by type jdbcTemplate; @Override public int update(String sql, Object[] param) { return jdbcTemplate.update(sql,param); } @Override public List<User> query(String sql, Object[] param) { return jdbcTemplate.query(sql,new BeanPropertyRowMapper<User>(User.class),param); }}Copy the code

4. Write the test class JdbcTemplateTest

package com.ch5.Test; import com.ch5.User; import com.ch5.dao.TestDao; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.List; public class JdbcTemplateTest { public static void main(String[] args) { ApplicationContext appCo = new ClassPathXmlApplicationContext("appliationContext.xml"); TestDao testDao=(TestDao)appCo.getBean("testDao"); String insertSql="insert into account values(null,? ,?) "; The Object param [] = {" chencheng ", 1050.0}; testDao.update(insertSql,param); String selectSql="select * from account"; List<User> list=testDao.query(selectSql,null); for (User user : list) { System.out.println(user); }}}Copy the code

Programmatic transaction management

The code that explicitly calls beginTransaction, COMMIT, rollback and other transaction-related methods is called programmatic transaction management. Programmatic transaction management works best when there are only a few transactions.

XML – based AOP implements transaction control

1. Write transaction management classes

package com.itheima.utils; /** * transaction management related utility class, which includes, start transaction, commit transaction, */ public class TransactionManager {private ConnectionUtils ConnectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } / open transaction * * * * / public void beginTransaction () {try {connectionUtils. GetThreadConnection () setAutoCommit (false); }catch (Exception e){ e.printStackTrace(); }} / * * * * / public affairs void commit () {try {connectionUtils. GetThreadConnection (), commit (); }catch (Exception e){ e.printStackTrace(); }} / rollback transaction * * * * / public void the rollback () {try {connectionUtils. GetThreadConnection (). The rollback (); }catch (Exception e){ e.printStackTrace(); }} / release link * * * * / public void release () {try {connectionUtils. GetThreadConnection (), close (); / / return connection pool connectionUtils. RemoveConnection (); }catch (Exception e){ e.printStackTrace(); }}}Copy the code

2. The configuration of aop

<! - configuration transaction manager - > < bean id = "txManager" class = "com. Itheima. Utils. TransactionManager" > <! <property name=" ConnectionUtils "></property> </bean> <aop:config> <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..) "/> <aop:aspect id="txAdvice" ref="txManager"> <! < AOP :before method="beginTransaction" pointcut-ref="pt1"/> <! Aop: post-returning method="commit" pointcut-ref=" pT1 "/> <aop: post-returning method="commit" pointcut-ref=" pT1 "/> <aop: post-returning method="commit" pointcut-ref=" pT1 "/> Aop :after-throwing method="rollback" pointcut ="pt1"/> <aop:after-throwing method="rollback" pointcut =" pT1 "/> <aop:after-throwing method="rollback" pointcut =" pT1 "/> <! <aop:after method="release" pointcut-ref="pt1"/> </aop:aspect> </aop:config>Copy the code

Programmatic transaction management based on the underlying API

Programmatic transaction management is based on the underlying API, according to the PlatformTransactionManager TransactionDefinition and TeansactionStatus core interface, through the way of programming for transaction management, The following example describes the transaction management implementation of the underlying API:

1. Configure the transaction manager for the data source

<! - configuration transaction manager - > < bean id = "txManager" class = ". Org. Springframework. JDBC datasource. DataSourceTransactionManager "> < property name="dataSource" ref="dataSource"/> </bean>Copy the code

2. Create a data access class

package com.ch5.dao.Impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Repository; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; @Repository("codeTransaction") public class CodeTransaction { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private DataSourceTransactionManager transactionManager; Public String testTransaction () {/ / the default transaction definition TransactionDefinition definition = new DefaultTransactionDefinition (); / / open transaction TransactionStatus TransactionStatus = transactionManager. GetTransaction (definition); String message=" Execute successfully, no rollback "; try{ String sql = "delete * from account"; String insertSql = "insert into account values(? ,? ,?) "; Object param[] = {"1","chenheng",2000}; jdbcTemplate.update(sql); // the id is repeated, so an error occurred. jdbcTemplate.update(insertSql,param); jdbcTemplate.update(insertSql,param); MIT (transactionStatus); / / to commit the transaction transactionManager.com } the catch (Exception e) {/ / abnormal, rollback transactionManager. Rollback (transactionStatus); Message =" transaction rollback "; e.printStackTrace(); } return message; }}Copy the code

3. Define test classes

package com.ch5.Test; import com.ch5.dao.Impl.CodeTransaction; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TransactionMangagerTest { public static void main(String[] args) { ApplicationContext appCo=new ClassPathXmlApplicationContext("appliationContext.xml"); CodeTransaction codeTransaction = (CodeTransaction)appCo.getBean("codeTransaction"); String result = codeTransaction.testTransaction(); System.out.println(result); }}Copy the code

Programmatic transaction management based on TransactionTemplate

The transaction code is scattered in the business logic code, breaking the organization of the original code, and every transaction has similar code for starting, committing, and rolling back transactions.

The Excute method of the TransactionTemplate takes an argument of type TransactionCallback interface, which defines a DoInTransaction method that typically implements the TransactionCallback interface as an anonymous inner class, And write the business logic code in its doInTransaction method. The default transaction commit and rollback rules can be used here, there is no need to explicitly call any transaction APIS in the business code, the doInTransaction method has a parameter of type TransactionStatus, The setRollbackOnly method of this parameter can be called at any point in the method to identify the transaction as rollback to perform the transaction rollback.

By default, if an unchecked exception is thrown during the execution of a callback method, or if the setRollbackOnly method is explicitly called, the transaction is rolled back; If the transaction completes or throws an exception of type Checked, the transaction is committed.

The steps for programmatic transaction management based on TransactionTemplate are as follows:

1. Add a transaction template for transaction management

Based on the underlying API to develop applicationContext. The XML configuration file using springframwork org, springframework, transaction. Support. TransactionTemplate class for something The transaction manager adds the transaction template. The complete configuration file is as follows:

<? The XML version = "1.0" encoding = "utf-8"? > <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <! <context:component-scan base-package="com.ch5"/> <! - configuration data source - > < bean id = "dataSource" class = ". Org. Springframework. JDBC dataSource. DriverManagerDataSource "> <! <property name="driverClassName" value=" com.mysql.jdbc.driver "/> <! - the connection url - > < property name = "url" value = "JDBC: mysql: / / localhost: 3306 / spring? characterEncoding=utf-8"/> <! Property name="username" value="root"/> <property name="password" value="root"/> </bean> <! - configuration transaction manager - > < bean id = "txManager" class = ". Org. Springframework. JDBC datasource. DataSourceTransactionManager "> < property name="dataSource" ref="dataSource"/> </bean> <! Create transactionTemplate for txManager --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="txManager"/> </bean> <! - configure a JDBC template - > < bean id = "jdbcTemplate" class = "org. Springframework. JDBC. Core. JdbcTemplate" > < property name = "dataSource" ref="dataSource"/> </bean> </beans>Copy the code

2. Create TransactionTemplateDao

package com.ch5; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; @Repository("transactionTemplateDao") public class TransactionTemplateDao { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private TransactionTemplate transactionTemplate; String message = ""; Public String TransactionTemplateTest(){// Implement the TransactionCallback interface in the same way you specify the inner class. Use the default transaction rules. transactionTemplate.execute(new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus status) { String insertSql = "insert into account values(? ,? ,?) "; Object param [] = {9, "Chen", 5000.0}; try{ jdbcTemplate.update(insertSql,param); jdbcTemplate.update(insertSql,param); Message =" Execution succeeded, no rollback "; }catch (Exception e){message=" transaction rollback "; } return message; }}); return message; }}Copy the code

3. Create a test class TransactionTemplateDaoTest

package com.ch5.Test; import com.ch5.TransactionTemplateDao; import com.ch5.dao.Impl.CodeTransaction; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TransactionTemplateDaoTest { public static void main(String[] args) { ApplicationContext appCo=new ClassPathXmlApplicationContext("appliationContext.xml"); TransactionTemplateDao transactionTemplateDao = appCo.getBean("transactionTemplateDao", TransactionTemplateDao.class); String result = transactionTemplateDao.TransactionTemplateTest(); System.out.println(result); }}Copy the code

Declarative transaction management

Spring’s declarative transaction management is transaction management implemented through AOP technology. Its essence is to intercept methods before and after, then create a transaction before the target method starts, and commit or roll back the transaction after execution.

The only disadvantage of declarative transactions compared to programmatic transaction management is that the finest granularity can only be applied at the method level, not at the code block level, as programmatic transaction management can, but even if there is a need for this, workararound can be used to address it. For example, a block of code that will be transacted can be wrapped separately as a method.

Spring’s declarative transaction management can be implemented in two ways, either xmL-based or using the @Transactional annotation

Declarative transaction management based on XML

Xml-based declarative transaction management is implemented by configuring transaction rule declarations in configuration files. Spring provides the TX namespace to configure transaction management, provides the TX: Advice element to configure transaction notification, and typically specifies the ID and transaction-manager attribute when configuring TX: Advice, where the ID is the unique identifier of the configuration file. Transaction-manager specifies the transaction manager. You also need to configure the TX: Attributes child, which can configure multiple TX: Method child elements to determine the details of the transaction to be executed.

After configuring the enhanced handling of transactions in the TX: Advice element, you can write an AOP configuration to have Spring automatically generate proxies for target objects. Here is an example of how Spring implements declarative transaction management in XML. In order to reflect the process of transaction management, create Dao, Service, controller layer 3 implementation.

1. Create Dao interfaces and implementation classes

package statment.dao;
 
public interface TestDao {
    public int save(String sql,Object param[]);
    public int delete(String sql,Object param[]);
}
Copy the code
package statment.dao.Impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import statment.dao.TestDao; @Repository("testDao") public class TestDaoImpl implements TestDao { @Autowired private JdbcTemplate jdbcTemplate; public int save(String sql, Object[] param) { return jdbcTemplate.update(sql,param); } public int delete(String sql, Object[] param) { return jdbcTemplate.update(sql,param); }}Copy the code

2. Create the Service interface and implementation class

package statment.Service;
 
public interface TestService {
    public void test();
}
Copy the code
package statment.Service.Impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import statment.Service.TestService; import statment.dao.TestDao; @Service("testService") public class TestServiceImpl implements TestService { @Autowired private TestDao testDao; public void test() { String deleteSql="delete from account"; String saveSql="insert into account values(? ,? ,?) "; Object param[] = {1,"shitji",5000}; testDao.delete(deleteSql,null); testDao.save(saveSql,param); }}Copy the code

Create the Controller class

package statment.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import statment.Service.TestService; @Controller public class StatementController { @Autowired private TestService testService; public void test(){ testService.test(); }}Copy the code

4. Write the configuration file bean.xml

<? The XML version = "1.0" encoding = "utf-8"? > <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <context:component-scan base-package="statment"/> <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/spring"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:advice id="myAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="*"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="txPonintCut" expression="execution(* statment.Service.*.*(..) )"/> <aop:advisor advice-ref="myAdvice" pointcut-ref="txPonintCut"/> </aop:config> </beans>Copy the code

5. Write test classes

package statment.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import statment.controller.StatementController; public class XMLTest { public static void main(String[] args) { ApplicationContext appCo=new ClassPathXmlApplicationContext("bean.xml"); StatementController controller = appCo.getBean("statementController", StatementController.class); controller.test(); }}Copy the code

Annotation-based declarative transaction management

package statment.Service.Impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import statment.Service.TestService; import statment.dao.TestDao; @Service("testService") @Transactional public class TestServiceImpl implements TestService { @Autowired private TestDao testDao; public void test() { String deleteSql="delete from account"; String saveSql="insert into account values(? ,? ,?) "; Object param[] = {1,"shitji",5000}; testDao.delete(deleteSql,null); testDao.save(saveSql,param); }}Copy the code

By adding @Transactional, you specify that this class needs to be transactionally managed by Spring. Note that this annotation only applies to methods that are public modified. 支那

Catch exceptions in a transaction

The flow of declarative transaction processing is:

1.Spring completes the transaction definition according to the configuration and sets transaction properties.

2. Implement developer code logic.

3. If the developer’s code produces an exception and meets the rollback configuration conditions, the transaction is rolled back, otherwise the transaction is committed.

4. Transaction resource release.

If developers add a try to their code logic… Catch statement, Spring cannot roll back a transaction normally in a declarative transaction. The reason is that Spring only rolls back a transaction when an uncaught RuntimeException occurs. So you have to deal with that.

Catch exceptions in DECLARative transaction management based on XML

** To catch exceptions in XML-based declarative transaction management, two additional steps are required: **

1. Modify the configuration of declared transactions

<tx:method name="*" rollback-for="java.lang.Exception"/>
Copy the code

Add throw new RuntimeException() to the catch statement;

Catch exceptions in annotation-based declarative transaction management

1. Modify the annotation content and add the rollbackFor property

@Transactional(rollbackFor = {Exception.class})
Copy the code

Add throw new RuntimeException() to the catch statement;

The last

I have arranged a: Spring+Spring family barrel information, relevant information documents and knowledge map, Java systematic information, (including Java core knowledge points, interview topics and the latest Internet real questions in 20 years, e-books, etc.) friends who need to pay attention to the public number [procedures Yuan Small wan] can be obtained.