JavaEE simple example – Spring’s transaction management

A brief introduction:

In the previous introduction to the Spring framework, we mentioned that there is a module called Data Access/Integration (Data Access/Integration), and in this module, in addition to jdbc, there is another important place, which is also where we learn this module The place that must be mastered is transaction management (Transaction).

First of all, let’s understand what a transaction is. A transaction is a concept in the database. It is mainly for the purpose that when we execute multiple SQL statements, if there is an error in one of the statements in the middle, all the previously executed successful statements will not be counted. All operations All are revoked, the most important thing is that if there is any operation on the data, all the parts will return to the state before the transaction started. This collection of multiple SQL statements is called a transaction, and the management of the transaction is mainly responsible for the execution of multiple SQL statements. If an error occurs at this time, it will help us set the state before the transaction starts.

Then let’s introduce transaction management in Spring. Spring provides an API dedicated to transaction management, which can easily help us manage transactions. We learn these APIs to better understand how to write our transaction manager in the XML configuration file. After all, we will mainly use the XML configuration file and annotations to declare transaction management.

How to use:

The first is the PlatformTransactionManager interface, which is translated as the Platform Transaction Manager. The main function of this interface is to manage things. The interface includes methods for obtaining transaction status, methods for committing transactions, and methods for rolling back transactions. In actual medical use, the management of Spring transactions is actually managed by collective persistent It is completed by modernization technology, and PlatformTransactionManager only provides a unified abstract method. For example, in Spring, the implementation class DataSourcePlatformTransactionManager is provided for Spring JDBC and Mybatis that rely on DataSource (data source) persistence technologies, so that we can manage our transactions through this class.

Then there is the TransactionDefinition interface, which defines constants used to describe transactions, including the isolation level of the transaction, the propagation behavior of the transaction, the timeout period of the transaction, and whether it is read-only. The isolation level of a transaction refers to the degree of isolation between transactions. For example, when I am executing a certain transaction, I have already started to modify the data, but this modification has not been submitted to the database, which means that the data in the database is not It has not started to be modified, and another transaction can come to access the data that I have not submitted. For the isolation level of a transaction, Spring provides five fixed constants, among which the more commonly used ones are: ISOLATION_REPEATBLE_READ: read committed, the data modified by a transaction can only be read by another transaction after it is submitted, which can avoid dirty reads ISOLATION_REPEATABLE_READ: default attribute , allows repeated reads, can avoid dirty reads, increases resource consumption, and is about the propagation behavior of transactions. The propagation behavior of transactions means that when we do business nesting, the outer business and the inner business are carried out Separate business management or combined into one business for management, the commonly used attributes are: PROPAGATION_REQUIRED: default value, if there is already a transaction, join the transaction; if there is no transaction, create a new transaction. Indicates that nested transactions will be merged. PROPAGATION_REQUIRS_NEW: Create a new transaction, if a transaction already exists, suspend the old transaction. It means that only one transaction management can be performed, and the outer transaction management is temporarily suspended when entering the inner transaction management, and the outer transaction management is continued after the inner transaction management is completed.

There are some other setting items, but we generally do not change these setting items. We only need to know the timeout period of the transaction if these configuration items exist. Get out of business. Whether it is read-only. When the transaction is read-only, the transaction does not have the ability to write data to the database. If data is modified in a read-only transaction, an exception will be thrown, which will help improve performance.

There is also the last TransactionStatus, which is mainly used for programmatic transaction management to define the status of the transaction, and will not be used in our declarative transaction management.

Code implementation:

After understanding these basics, we can start to enter our process of writing configuration files. First of all, if we are using XML configuration files for the first time to configure declarative transaction management, then we must first enter the pom file Introduce dependencies in

<!-- Spring's core dependency coordinates -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.21.RELEASE</version>
        </dependency>
<!-- spring Jdbc package -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.21.RELEASE</version>
        </dependency>
<!-- Spring's dependency coordinates for transaction management -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.21.RELEASE</version>
        </dependency>
<!-- Used to parse pointcut expressions -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.7.2</version>
        </dependency>

Then add transaction management-related constraints in our Bean management XML configuration file

<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:bean="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

Continue to add the dataSource class in the Bean management XML configuration file. We will call it the data source class in the future. We don’t like foreign languages. We need to add four attributes in the data source class.

Respectively, driverClassName: Indicates the class driven by the database

url: Indicates the link address of the database

username: Indicates the username of the database

password: Indicates the password of the database user

These are all the parameters we had to log in to the database before.

<!-- Create data source object -->
    <bean id="dateSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/jdbc"/>
        <property name="username" value="root"/>
        <property name="password" value="@gaoyang1"/>
    </bean>

The jdbcTemplate class is used to define the template class, because this class has the methods we need to operate the database, and we need to inject the data source class into the dabaSource attribute in the template class through dependency injection

<!-- Create template class object -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dateSource"/>
    </bean>

Custom database operation class, this class is written by ourselves, this class must contain an attribute, the type of the attribute must be the type of the template class, and then we will inject the template class we just configured into our In the jdbcTemplat attribute of the custom database operation class and our DataSourceTransactionManager transaction management class, this class is the database management class implemented by Spring based on dataSource that we introduced earlier, indicating that this class is also dependent on the data source, so we also need to use the previous The data source class is injected into the dataSource attribute of the transaction management class through dependency injection

<!-- Create an object of a custom database operation class, and in the end we only need or this object -->
    <bean id="manipulatingDatabase" class="Semester_4.SpringJDBC.TransactionManagement.ByNote.ManipulatingDatabase">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>
package Semester_4.SpringJDBC.TransactionManagement.ByConfigurationFile;

import org.springframework.jdbc.core.JdbcTemplate;

public class ManipulatingDatabase {
    private JdbcTemplate jdbcTemplate;

    @Override
    public String toString() {
        return "ManipulatingDatabase{" +
                "jdbcTemplate=" + jdbcTemplate +
                '}';
    }

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public ManipulatingDatabase(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public ManipulatingDatabase() {
    }
// Simulate a transfer transaction. In Java, transaction encapsulation is done through methods, that is, multiple SQL statements are placed in the same method, and this method becomes a transaction
    public void Transfer(String payer, String payee, int money){
// Define the SQL statement for data adjustment
        String SQL1 = "update user set money = money - ? where name = ?";
        String SQL2 = "update user set money = money + ? where name = ?";
        this.jdbcTemplate.update(SQL1,money,payer);
// Simulate when executing a transaction, if an exception occurs in the middle
        int i = 1/0;
        this.jdbcTemplate.update(SQL2,money,payee);
    }
}

In addition to defining the object of the JDBC operation class, this class also needs to create a method that encapsulates the transaction

Transaction management based on XML configuration files is actually taking our transaction management class as an aspect and enhancing it into our existing transaction methods. Therefore, we can regard the transaction management class as an aspect, but this aspect is used before Some special configuration is required

<!-- Then start to configure transaction management -->
    <tx:advice id="interceptor" transaction-manager="transactionManager">
        <tx:attributes>
<!-- name is not the meaning of the name, but to specify the matching method name -->
<!-- Indicates that this transaction is readable and writable, using the default isolation level, using the default propagation behavior -->
            <tx:method name="*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!-- Then use the transaction manager as an aspect to insert enhancements into existing methods -->
    <aop:config>
<!-- Configuration entry point, the entry point is our transfer method -->
        <aop:pointcut id="pointcut" expression="execution(* Semester_4.SpringJDBC.TransactionManagement.ByConfigurationFile.ManipulatingDatabase.*(..))"/>
<!-- Configuration aspect, because our entry point and aspect have been written, and because our aspect is a transaction manager, so he also encapsulates the relationship with enhancement and entry point, so we only need to go Just import -->
        <aop:advisor advice-ref="interceptor" pointcut-ref="pointcut"/>
    </aop:config>

All our operations on the transaction management class are all under the tx:advice root tag. This root tag has two attributes, and the id is the unique identifier. The value of the transaction_manager attribute is the id value of our transaction management class just now, indicating that Refer to this class for transaction management under the root tag, which is the tx:attributes tag. This tag is mainly used to contain multiple tx:methods, and tx:methods is the tag used to set transaction management. It is mainly used to set our previous As I said, such attributes as isolation level and propagation behavior

The important attributes of tx:methods are as follows:

name: used to specify the matching pattern of the method name, this attribute is a required attribute

propagation: Specify the propagation scope of the transaction

isolation: Specifies the isolation level of the transaction

read-only: Specifies whether the transaction is read-only

Then we also need to define a class for encapsulating query results and register it in IoC:

<!-- Create a bean of the result set mapping class-->
    <bean id="user" class="Semester_4.SpringJDBC.TransactionManagement.ByNote.user"/>
package Semester_4.SpringJDBC.TransactionManagement.ByConfigurationFile;

public class user {
    private int id;
    private String name;
    private int money;

    @Override
    public String toString() {
        return "user{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }

    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 int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public user(int id, String name, int money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public user() {
    }
}

Finally, let’s summarize all the content in our Bean management XML configuration file:


<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:bean="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    
        
        
        
        
    
    <!-- Create template class object -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dateSource"/>
    </bean>
    
    
        
    
<!-- Create a bean of the result set mapping class-->
    <bean id="user" class="Semester_4.SpringJDBC.TransactionManagement.ByNote.user"/>

    

        
    

<!-- Then start to configure transaction management -->
    <tx:advice id="interceptor" transaction-manager="transactionManager">
        <tx:attributes>
<!-- name is not the meaning of the name, but to specify the matching method name -->
<!-- Indicates that this transaction is readable and writable, using the default isolation level, using the default propagation behavior -->
            <tx:method name="*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!-- Then use the transaction manager as an aspect to insert enhancements into existing methods -->
    <aop:config>
<!-- Configuration entry point, the entry point is our transfer method -->
        <aop:pointcut id="pointcut" expression="execution(* Semester_4.SpringJDBC.TransactionManagement.ByConfigurationFile.ManipulatingDatabase.*(..))"/>
<!-- Configuration aspect, because our entry point and aspect have been written, and because our aspect is a transaction manager, so he also encapsulates the relationship with enhancement and entry point, so we only need to go Just import -->
        <aop:advisor advice-ref="interceptor" pointcut-ref="pointcut"/>
    </aop:config>

Then create a test class and test it:

package Semester_4.SpringJDBC.TransactionManagement.ByConfigurationFile;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;

import java.util.List;

public class test {
    public static void main(String[] args) {
// create IoC container
        ApplicationContext ac = new ClassPathXmlApplicationContext("TeansactionManager.xml");
// Get the object of the custom database operation class
        ManipulatingDatabase md = (ManipulatingDatabase)ac.getBean("manipulatingDatabase");
// Create result set wrapper class
        RowMapper<user> mapper = new BeanPropertyRowMapper<>(user. class);
// Output the code before modification
        System.out.println("The data before changing:");
        List<user> queryBefore = md.getJdbcTemplate().query("select * from user", mapper);
        for(user u : queryBefore){
            System.out.println(u.toString());
        }
// Call the method to modify multiple pieces of data in the database
        md.Transfer("Zhang San","Li Si",100);
// Output the modified result
        List<user> queryAfter = md.getJdbcTemplate().query("select * from user", mapper);
        System.out.println("Data after change:");
        for(user u : queryAfter){
            System.out.println(u.toString());
        }
    }
}

Operation result:

First, let’s look at the data prepared in the database:

There are three people in the database, and their money is 300 at this time. Now we need to realize a requirement, which is to transfer Zhang San’s money to Li Sibai.

Normally, our logic is that Zhang San’s money will be reduced by 100, and Li Si’s money will be increased by 100. We start to write our method according to this logic:

// Simulate a transfer transaction. In Java, transaction encapsulation is done through methods, that is, multiple SQL statements are placed in the same method, and this method becomes a transaction
    public void Transfer(String payer, String payee, int money){
// Define the SQL statement for data adjustment
        String SQL1 = "update user set money = money - ? where name = ?";
        String SQL2 = "update user set money = money + ? where name = ?";
        this.jdbcTemplate.update(SQL1,money,payer);
// Simulate when executing a transaction, if an exception occurs in the middle
        int i = 1/0;
        this.jdbcTemplate.update(SQL2,money,payee);
    }

We normally call the method to update the data, but if an error occurs during the update, the execution of the method will fail. At this time, if we do not do transaction management, the following situation will occur:

The method reports an error and the program terminates, but the data in the database has changed:

Because we have executed a SQL statement and written data to the database before the method failed, which will cause errors in our data, now we add transaction management:

Normal or simulated error reporting, the method is terminated, let’s continue to look at the data in the database:

in the database The data has not changed, because the method reports an error, the transaction management will detect, and only after the entire method is successfully executed, the data modification will be written into the database to complete the data persistence operation, so if an exception occurs in the method As a result, the method cannot be executed smoothly, and the transaction manager will not allow data modification to be written to the database. This is the basic logic of transaction management.

Points to note:

The knowledge points about this chapter are mainly in the configuration of the transaction manager, and how to link the transaction management with the method that needs to be managed by the transaction, mainly using the aspect enhancement method.

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge Java skill treeHomepageOverview 108415 people are studying systematically