Spring's management of things

Keywords: JavaEE Spring Back-end

What is a thing

Things can be regarded as a unit composed of several operations on the database at a time.

When we are developing projects at the enterprise level, we are faced with an operation of business personnel, but actually a collection of multi-step operations to read and write to the database. Because data operations are executed sequentially, exceptions may occur in any step of operation. Exceptions will cause the following steps to complete. But at this time, the business logic is not completed correctly (go through the whole process). The previous operation data results may not be reliable at all, so in this case, you should go back.

The function of transaction is to ensure that every operation of the user is reliable, and every operation in the transaction must be successfully executed. As long as an exception occurs, it will fall back to the state of no operation at the beginning of the transaction. These operations are either completed successfully or cancelled, so as to ensure that the data meets the requirements of consistency.

Classification of transactions

Transaction management in Spring can be divided into two forms:

  1. Programming transaction
  2. Declarative transaction

Programming transaction

It is rarely used in projects. This method needs to inject a transaction management object TransactionTemplate, and then we need to write our own code to implement when we need to commit or roll back transactions in our code.
It is in our code that we need to manually (shown) commit the rollback transaction like Mybatis.

Declarative transaction

This is based on AOP. In essence, it is an interception before and after methods, so declarative transactions are method level.

There are two ways of Spring declarative transaction management:

  1. xml based configuration
  2. Annotation based implementation

Code demonstration:
This is the general control of Spring

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--Note the addition of constraints-->


    <!--open Spring The annotation scan parses it-->
    <!--write package Be sure to pay attention to your own package name. Everything under the advanced part can be scanned-->
    <context:component-scan base-package="com.demo.spring"></context:component-scan>

    <import resource="db.xml"></import>
    <!--Import db.xml Configuration file for-->
</beans>
<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--Note the addition of constraints-->

    <!--Configure configuration related to database connection-->

    <!--Spring Read in properties file-->
    <context:property-placeholder location="config.properties"></context:property-placeholder>

    <!--to configure Druid Database connection object for-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${ume}"></property>
        <property name="password" value="${pwd}"></property>
        <property name="initialSize" value="${initialSize}"></property>
        <!--initialSize Initialize several-->
        <property name="maxActive" value="${maxActive}"></property>
        <!--maxActive How many connection pools can you create-->
    </bean>

    <!--Spring Provided JdbcTemplate Encapsulation class-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"></property>
        <!--Will Druids Druid Inject into Spring Provided JdbcTemplate Middle go-->
    </bean>
</beans>

If no transaction is added, in this case:

package com.demo.spring.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository(value = "userDao")
public class UserDao {

    @Autowired
    JdbcTemplate jdbcTemplate;
    public void save() {
        jdbcTemplate.update("insert into admin(account,password,sex) values (?,?,?)","jim","111","male");
        int a = 10/0;
        jdbcTemplate.update("insert into admin(account,password,sex) values (?,?,?)","tom","111","female");
    }
}

Run test code:

package com.demo.spring.test;

import com.demo.spring.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

        UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
        userDao.save();
    }
}

The database will still run the first request sent to the database, and its smallest unit is JDBC template

Now we want the save method in the UserDao class as a whole (in the unit of the whole) to execute or not to execute, which leads to transactions.

Configuration declarative transaction based on xml

Configure using xml

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--Note the addition of constraints-->

    <!--Configure configuration related to database connection-->

    <!--Spring Read in properties file-->
    <context:property-placeholder location="config.properties"></context:property-placeholder>

    <!--to configure Druid Database connection object for-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${ume}"></property>
        <property name="password" value="${pwd}"></property>
        <property name="initialSize" value="${initialSize}"></property>
        <!--initialSize Initialize several-->
        <property name="maxActive" value="${maxActive}"></property>
        <!--maxActive How many connection pools can you create-->
    </bean>

    <!--Spring Provided JdbcTemplate Encapsulation class-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"></property>
        <!--Will Druids Druid Inject into Spring Provided JdbcTemplate Middle go-->
    </bean>




    <!--Declarative transaction:
    Spring Configure transaction management-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--Connection to database-->
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>

    <!--to configure Spring Communication behavior, this is Spring A unique function of the framework-->
    <tx:advice id="txadvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!--Adopted here Spring of AOP To add transaction management to the method-->
    <aop:config>
        <aop:pointcut id="save" expression="execution(* com.demo.spring.dao.UserDao.save(..))"/>
        <aop:advisor advice-ref="txadvice" pointcut-ref="save"></aop:advisor>
    </aop:config>

</beans>

Under the management of xml, if you run the code again, you will not let the database execute all the code in case of exceptions.

No more entries in the database

Delete manually written exceptions

Execute again and the results are as follows:

Implemented by annotation

Enable annotation transaction mode:

<!--Enable annotation mode management transaction management-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

Write in the configuration file

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--Note the addition of constraints-->

    <!--Configure configuration related to database connection-->

    <!--Spring Read in properties file-->
    <context:property-placeholder location="config.properties"></context:property-placeholder>

    <!--to configure Druid Database connection object for-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${ume}"></property>
        <property name="password" value="${pwd}"></property>
        <property name="initialSize" value="${initialSize}"></property>
        <!--initialSize Initialize several-->
        <property name="maxActive" value="${maxActive}"></property>
        <!--maxActive How many connection pools can you create-->
    </bean>

    <!--Spring Provided JdbcTemplate Encapsulation class-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"></property>
        <!--Will Druids Druid Inject into Spring Provided JdbcTemplate Middle go-->
    </bean>


    <!--Declarative transaction:
    Spring Configure transaction management-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--Connection to database-->
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>

	<!--Enable annotation mode management transaction management-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>

@The Transactional annotation tag is added to the class, indicating that all methods in the class are added with transaction management. If it is added to the method, the table will the currently added method.

package com.demo.spring.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository(value = "userDao")
public class UserDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

//    Transaction: it is regarded as a sequence composed of several operations on the database at a time. It is a whole process, either successful or failed
//    By default, the jdbc template uses the transaction submission method of jdbc, and is automatically submitted after execution

//    When we save once, it includes two or more operations on the database
//    Then we consider these operations as a unit (whole)
//    This is the atomicity (indivisibility) of transactions. Multiple operations either succeed or fail
    @Transactional
    public void save() {
        jdbcTemplate.update("insert into admin(account,password,sex) values (?,?,?)","jim","111","male");
//        int a = 10/0;
        jdbcTemplate.update("insert into admin(account,password,sex) values (?,?,?)","tom","111","female");
    }
}


Execute in the form of annotation label, and the result is:

Transaction propagation behavior in Spring

In our actual development, although the user has one operation, there are actually multiple methods at the bottom to implement the operation on the database, so we need to manage the transaction of multiple methods.

An operation of the user

package com.demo.spring.service;

import com.demo.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service(value = "userService")
public class UserService {

    @Autowired
    private UserDao userDao;

    //Here, userdao is assigned to the userdao attribute in UserService through injection in Spring
    // Then it is used directly in UserService
    public void saveUser() {
        System.out.println("This is UserService Medium saveUser Methods in");
        userDao.save1();
        int a = 10 / 0;
        userDao.save2();
    }
}

The bottom layer needs to operate two methods to implement

package com.demo.spring.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository(value = "userDao")
public class UserDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

//    Transaction: it is regarded as a sequence composed of several operations on the database at a time. It is a whole process, either successful or failed
//    By default, the jdbc template uses the transaction submission method of jdbc, and is automatically submitted after execution

//    When we save once, it includes two or more operations on the database
//    Then we consider these operations as a unit (whole)
//    This is the atomicity (indivisibility) of transactions. Multiple operations either succeed or fail
    @Transactional
    public void save1() {
        jdbcTemplate.update("insert into admin(account,password,sex) values (?,?,?)","jim","111","male");
    }

	@Transactional
    public void save2(){
        jdbcTemplate.update("insert into admin(account,password,sex) values (?,?,?)","tom","111","female");

    }
}

Test code run:

package com.demo.spring.test;

import com.demo.spring.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.saveUser();
    }
}

result:

If there is an error in UserService, the transaction management here will fail. Based on this problem, as long as we add the @ Transactional tag to UserService, he can't enter. Adding to a class means that all classes are in transaction management. On a person's method, it means that this method is carried out in transaction management.

package com.demo.spring.service;

import com.demo.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "userService")
public class UserService {

    @Autowired
    private UserDao userDao;

    //Here, userdao is assigned to the userdao attribute in UserService through injection in Spring
    // Then it is used directly in UserService
    
    @Transactional
    public void saveUser() {
        System.out.println("This is UserService Medium saveUser Methods in");
        userDao.save1();
        int a = 10/0;
        userDao.save2();
    }
}

Then the test will be OK.

However, we still need to consider the transaction relationship between the two underlying methods.

What is the propagation behavior of transactions?

Since it is called communication, there must be at least two things before communication can occur. Monomer does not exist to spread this behavior. Transaction propagation behavior refers to how a transaction should proceed when its method is called by another transaction's method. Transaction propagation behavior is a unique transaction enhancement feature of the Spring framework. It does not enter the database behavior of the method actually provided by the transaction.

For example, when the methodA transaction method calls the methodB transaction method, does methodB continue to call the transaction of this methodA or start a transaction management, which is determined by the transaction propagation behavior of methodB.

Profile:
config.properties property file:

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
ume=root
pwd=wasd
initialSize=5
maxActive=15

Configure database connection and transaction management:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--Note the addition of constraints-->


    <!--Configure configuration related to database connection-->

    <!--Spring Read in properties file-->
    <context:property-placeholder location="config.properties"></context:property-placeholder>

    <!--to configure Druid Database connection object for-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${ume}"></property>
        <property name="password" value="${pwd}"></property>
        <property name="initialSize" value="${initialSize}"></property>
        <!--initialSize Initialize several-->
        <property name="maxActive" value="${maxActive}"></property>
        <!--maxActive How many connection pools can you create-->
    </bean>

    <!--Spring Provided JdbcTemplate Encapsulation class-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"></property>
        <!--Will Druids Druid Inject into Spring Provided JdbcTemplate Middle go-->
    </bean>


    <!--Turn on transaction management-->
    <!--Declarative transaction: Spring Configure transaction management-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--Connection to database-->
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>

    <!--Enable annotation mode management transaction management-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    
</beans>

General Spring configuration file:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--Note the addition of constraints-->


    <!--open Spring The annotation scan parses it-->
    <!--write package Be sure to pay attention to your own package name. Everything under the advanced part can be scanned-->
    <context:component-scan base-package="com.demo.spring"></context:component-scan>

    <import resource="db.xml"></import>
    <!--Import db.xml Configuration file for-->
</beans>

Code demonstration:

dao layer:

package com.demo.spring.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class DeptDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

    public void save(){
        jdbcTemplate.update("insert into dept (name,adminId) values (?,?)","R & D department",1);
    }
}
package com.demo.spring.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class CommonDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

    public void save(){
        jdbcTemplate.update("insert into test(id) values (?)",2);
    }
}

service layer:

package com.demo.spring.service;

import com.demo.spring.dao.CommonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "commonService")
public class CommonService {

    @Autowired
    CommonDao commonDao;

    @Transactional
    public void saveCommon(){
        commonDao.save();
    }
}

In this method, the transaction on the opposite side of A calls B. B is also A transaction, so how does B proceed.

package com.demo.spring.service;

import com.demo.spring.dao.DeptDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


@Service(value = "deptService")
public class DeptService {
    
    @Autowired
    DeptDao deptDao;

    @Autowired
    CommonService commonService;

    @Transactional//Add A as transaction management
    public void saveDept(){
        deptDao.save();
        commonService.saveCommon();//Call B
        //Then there is a relative contradiction. A and B have transactions, and a calls the B method.
        // Then, how should B execute A transaction? Here is the transaction execution
    }
}

Adding and writing method of transaction propagation mode:
@Transactional(propagation=Propagation.REQUIRED)

Seven multicast behaviors are defined in Spring.

Transaction propagation behaviorexplain
PROPAGATION_REQUIREDIf there is no current transaction, create a new transaction. If there is already a transaction, join it. This is the most common choice.
PROPAGATION_SUPPORTSThe current transaction is supported. If there is no current transaction, it will be executed in a non transactional manner.
PROPAGATION_MANDATORYUse the current transaction. If there is no current transaction, throw an exception.
PROPAGATION_REQUIRES_NEWCreate a new transaction. If there is a current transaction, suspend the current transaction.
PROPAGATION_NOT_SUPPORTEDThe operation is performed in a non transactional manner. If there is a current transaction, the current transaction is suspended.
PROPAGATION_NEVERExecute in a non transactional manner. If a transaction currently exists, an exception will be thrown.
PROPAGATION_NESTEDIf a transaction currently exists, it is executed within a nested transaction. If there is no current transaction, execute the same as the deployment_ Required similar operations.

PROPAGATION_REQUIRED

If there is no current transaction, create a new transaction. If there is already a transaction, join it.
From front to back, there is and only one transaction created. A and B have only one transaction, which can be understood as that they are all in one container. They want to live together and die together. The premise is that the label of transaction propagation is written (because promotion_required is also a default transaction processing method)

The specified method must be executed within a transaction. If there is a current transaction, add it to the current transaction. If there is no current transaction, a transaction is created and executed in the transaction. The blogging behavior in this case is the most common and the default propagation behavior of Spring.

Code demonstration:

If (int a = 10/0) errors occur in B's transaction management, the theory is that there is no data saved in the database

package com.demo.spring.service;

import com.demo.spring.dao.CommonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class CommonService {

    @Autowired
    CommonDao commonDao;

    //If the communication behavior of a and B is REQUIRED, B will directly join the communication behavior of A
    // If A has no propagation behavior and B has, then B will create A separate transaction
    //If something goes wrong in B
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveCommon(){
        commonDao.save();
        int a = 10/0;
    }
}
package com.demo.spring.service;

import com.demo.spring.dao.DeptDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


@Service(value = "deptService")
public class DeptService {

    @Autowired
    DeptDao deptDao;

    @Autowired
    CommonService commonService;
    
    @Transactional(propagation = Propagation.REQUIRED)//Add A as transaction management
    public void saveDept(){
        deptDao.save();
        commonService.saveCommon();//Call B
        //Then there is a relative contradiction. A and B have transactions, and a calls the B method.
        // Then, how should B execute A transaction? Here is the transaction execution
    }
}

Code of test run result:

package com.demo.spring.test;

import com.demo.spring.service.DeptService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

        DeptService deptService = applicationContext.getBean("deptService", DeptService.class);
        
        deptService.saveDept();
    }
}

As a result, no input is made in the database. There are errors. Although they are not in the same method, they are managed by (REQUIRED) transactions. Therefore, they can be regarded as one method. They must make mistakes together, and the execution results are all right before they can save the data in the database.

If A does not start the transaction at all, the data in A will be stored in it, but B still cannot. If A transaction is started, one of its errors cannot be stored in the database.

PROPAGATION_SUPPORTS

The current transaction is supported. If there is no current transaction, it will be executed in a non transactional manner.
If A has A transaction, B has A transaction, and the transaction propagation is the same as B (that is, in A container). If A doesn't, then B doesn't.

A transaction exists:

package com.demo.spring.service;

import com.demo.spring.dao.DeptDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


@Service(value = "deptService")
public class DeptService {

    @Autowired
    DeptDao deptDao;

    @Autowired
    CommonService commonService;

    @Transactional(propagation = Propagation.REQUIRED)//Add A as transaction management
    public void saveDept(){
        deptDao.save();
        commonService.saveCommon();//Call B
        //Then there is a relative contradiction. A and B have transactions, and a calls the B method.
        // Then, how should B execute A transaction? Here is the transaction execution
    }
}

The transaction of B is SUPPORTS, and error is in B

package com.demo.spring.service;

import com.demo.spring.dao.CommonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "commonService")
public class CommonService {

    @Autowired
    CommonDao commonDao;

    //If the propagation mode of A is REQUIRED and the propagation mode of B is SUPPORTS, then B will join A for execution
    //What is A and what is B
    @Transactional(propagation = Propagation.SUPPORTS)
    public void saveCommon(){
        commonDao.save();
        int a = 10/0;
    }
}

The result is that neither of the two input data to the database will successfully enter the data into the database.

If A has no transactions:

package com.demo.spring.service;

import com.demo.spring.dao.DeptDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


@Service(value = "deptService")
public class DeptService {

    @Autowired
    DeptDao deptDao;

    @Autowired
    CommonService commonService;

    public void saveDept(){
        deptDao.save();
        commonService.saveCommon();//Call B
        //Then there is a relative contradiction. A and B have transactions, and a calls the B method.
        // Then, how should B execute A transaction? Here is the transaction execution
    }
}

B still uses SUPPORTS.
Then, both of them have no transaction processing and are executed step by step according to the normal execution data. When the execution is wrong, the opportunity stops.

The result is that although there are errors, both A and B execute and save the database code. (because error 10 / 0 in B is after code execution).

PROPAGATION_REQUIRES_NEW

Create a new transaction. If there is a current transaction, suspend the current transaction.

When A calls transaction B, whether A has A transaction or not, B will create A transaction.
If A has no transaction, then B is A single small transaction.
If A has A transaction, B creates another transaction. During execution, A transaction is suspended and B transaction is executed. Both A and B have transactions, but the two transactions are irrelevant. They don't affect each other.

The transaction processing method of A is still the default transaction processing, but there will be an error in A

package com.demo.spring.service;

import com.demo.spring.dao.DeptDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


@Service(value = "deptService")
public class DeptService {

    @Autowired
    DeptDao deptDao;

    @Autowired
    CommonService commonService;

    @Transactional(propagation = Propagation.REQUIRED)//Add A as transaction management
    public void saveDept(){
        deptDao.save();
        commonService.saveCommon();//Call B
        int a = 10/0;
        //The code is from top to bottom. int a = 10/0 is the problem code
        // But before that, B was called, so B was a separate transaction when it was executed.
        //So B should be able to store the code normally, while A can't.
    }
}
package com.demo.spring.service;

import com.demo.spring.dao.CommonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "commonService")
public class CommonService {

    @Autowired
    CommonDao commonDao;

    //A's mode of transmission is REQUIRED, and B's mode of transmission is requirements_ NEW
    // , which means that a new transaction is created, which is a separate transaction,
    // B will not be affected by A
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveCommon(){
        commonDao.save();
    }
}

A new transaction is created in the transaction of B. As long as B is called, it will execute, and the execution of B will not be affected by A.

Operation results:
No data is stored in the dept table
Data can be stored in the test table:

Scenario in which the transaction takes effect

  1. Calling a method in the same class will cause @ Transactional to fail
  2. @Transactional is applied to non public modification methods, that is, it can only be used on public modification
  3. @Transaction annotation attribute propagation setting error (not a reasonable attribute required by you)
  4. The exception caught by catch leads to @ Transactional failure (it should be caught by catch, so it can be understood that catch will solve this problem and make the code appear to be free of problems)
  5. The database engine does not support transactions (only InnoDB engine supports transactions in Mysql)

Posted by Ohio Guy on Mon, 29 Nov 2021 04:14:27 -0800