[Spring] AOP Transaction Management

Time:2023-11-18

I. AOP transaction management

1.Introduction to Spring transactions

1. Introduction to relevant concepts

  • Transaction role: in the data layer to ensure that a series of database operations with the success of the same failure
  • Spring Transaction Role: The data layer orbusiness layer Guarantees that a series of database operations will both succeed and fail
We can understand that there are transactions in the data layer, so why does the business layer need to deal with transactions as well? A simple example.
  • The transfer operation will have two data layer calls, one for adding money and one for subtracting money
  • By putting the transaction in the data layer, there are two transactions for adding and subtracting money
  • There’s no way to guarantee that adding and subtracting money will work or fail at the same time.
  • This is when transactions need to be handled at the business level.
Spring provides a platform transaction manager in order to manage transactionsPlatformTransactionManager [Spring] AOP Transaction Management commit is used to commit the transaction, rollback is used to rollback the transaction. PlatformTransactionManager is just an interface for which Spring also provides a concrete implementation:. [Spring] AOP Transaction Management As you can see from the name , we only need to give it a DataSource object , it can help you to go in the business layer to manage transactions . Its internal use of JDBC transactions. So if you are using the persistence layer is JDBC-related technology, you can use this transaction manager to manage your transactions. Mybatis internal use is JDBC transactions , so the later we Spring integration of Mybatis on the use of this DataSourceTransactionManager transaction manager .

2. Transfer cases — needs analysis

Next, through a case study to learn how Spring is to manage transactions. Let’s start by analyzing the requirements. Requirement: Realize the transfer operation between any two accounts. Demand minimization: less money in account A, more money in account B In order to realize the above business requirements, we can follow the steps below. ①: the data layer provides basic operations, the designated account to reduce money (outMoney), the designated account to add money (inMoney) ②: The business layer provides the transfer operation (transfer), calling the operation of reducing and adding money. ③: Provide two account numbers and operating amounts to perform the transfer operation. ④: Based on the Spring integration MyBatis environment to build the above operations

3. Transfer cases — environment setup

Step 1: Prepare database tables
We have already created this table when we integrated Mybatis, so we can use it directly.
create database spring_db character set utf8;
use spring_db;
create table tbl_account(
    id int primary key auto_increment,
    name varchar(35),
    money double
);
insert into tbl_account values(1,'Tom',1000);
insert into tbl_account values(2,'Jerry',1000);
Step 2:Create a project to import the jar package
Add dependencies to the project’s pom.xml.
<dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

  </dependencies>
Step 3: Create a model class based on the table
public class Account implements Serializable {

    private Integer id;
    private String name;
    private Double money;
	//setter... getter... toString... Summary of methods    
}
Step 4: Create the Dao interface
public interface AccountDao {

    @Update("update tbl_account set money = money + #{money} where name = #{name}")
    void inMoney(@Param("name") String name, @Param("money") Double money);

    @Update("update tbl_account set money = money - #{money} where name = #{name}")
    void outMoney(@Param("name") String name, @Param("money") Double money);
}
Step 5: Create Service Interface and Implementation Classes
public interface AccountService {
    /**
     * :: Transfer operations
     * @param out outgoing
     * :: @param in transferring parties
     * @param money amount
     */
    public void transfer(String out,String in ,Double money) ;
}

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void transfer(String out,String in ,Double money) {
        accountDao.outMoney(out,money);
        accountDao.inMoney(in,money);
    }

}
Step 6: Add the jdbc.properties file
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=root
Step 7: Create JdbcConfig configuration class
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}
Step 8: Create MybatisConfig configuration class
public class MybatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.itheima.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.itheima.dao");
        return msc;
    }
}
Step 9: Create SpringConfig configuration class
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
Step 10: Write Test Classes
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() throws IOException {
        accountService.transfer("Tom","Jerry",100D);
    }

}
The final structure of the created project is as follows. [Spring] AOP Transaction Management

4. Management of affairs

The above environment, running the unit test class, will perform the transfer operation thatTomof the account would be reduced by 100.JerryThe account will add 100. This is the result of running under normal circumstances, but if an exception occurs during the transfer, such as.
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void transfer(String out,String in ,Double money) {
        accountDao.outMoney(out,money);
        int i = 1/0;
        accountDao.inMoney(in,money);
    }

}
This time it simulates an anomaly in the transfer process, and the correct operation should be that the transfer has gone wrong.TomIt should still be 900.JerryIt should still be 1100, but when you actually run it, you’ll realize that it’s not what we thought it would be.TomThe account is 800 andJerryOr 1,100,100 bucks out of thin air. The bank’s happy. If you change the order of transfers, the bank should cry. In either case, it is not allowed to occur, and we do an analysis of the results just presented: the ①: When the program is executed normally, the account amount A minus B plus, no problem ②: The transfer fails after an exception occurs in the program, but the operation succeeds before the exception, and the operation fails after the exception, and the overall business fails. When the program goes wrong, we need to let the transaction to rollback, and this transaction should be added to the business layer, and Spring’s transaction management is used to solve these kinds of problems. Spring transaction management is implemented in the following steps.
Step 1: Add annotations to the methods that need to be managed by the transaction.
public interface AccountService {
    /**
     * :: Transfer operations
     * @param out outgoing
     * :: @param in transferring parties
     * @param money amount
     */
    // Configure the current interface method to have a transaction
    public void transfer(String out,String in ,Double money) ;
}

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;
	@Transactional
    public void transfer(String out,String in ,Double money) {
        accountDao.outMoney(out,money);
        int i = 1/0;
        accountDao.inMoney(in,money);
    }

}
Caution: The @Transactional can be written on interface classes, interface methods, implementation classes and implementation methods
  • Written on an interface class, all methods of all implementing classes of that interface will have transaction
  • Written on an interface method, all implementations of that method in all classes of that interface will have the transaction
  • Written on an implementation class, all methods in that class will have transactions
  • Write on the implementation class method that has transactions on it
  • It is recommended to write it on the implementing class or the methods of the implementing class
Step 2: Configure the transaction manager in the JdbcConfig class
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }

    //configure the transaction manager, mybatis uses jdbc transactions
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}
**Note: **Transaction manager to be selected according to the use of technology, Mybatis framework uses the JDBC transaction, you can directly use theDataSourceTransactionManager
Step 3: Turn on transaction annotations
In the SpringConfig configuration class turn on the
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class
// Turn on annotated transaction drivers
@EnableTransactionManagement
public class SpringConfig {
}
Step 4: Run the test class
It will be found that after an error in the converted business, the transaction will be able to control the recall and ensure the correctness of the data.
Knowledge point 1: @ EnableTransactionManagement
name @EnableTransactionManagement
typology Configuration class annotations
position Above the configuration class definition
corresponds English -ity, -ism, -ization Setting up the current Spring environment to enable annotated transaction support
Point # 2: @Transactional
name @Transactional
typology Interface annotations Class annotations Method annotations
position Above Business Layer Interfaces Above Business Layer Implementation Classes Above Business Methods
corresponds English -ity, -ism, -ization Add a transaction to the current business tier method (all methods in the class or interface if set above the class or interface)

2. Spring transaction roles

In this section we focus on understanding two concepts, which areAdministratorandCoordinator of services
  1. Before Spring transactions are turned on.
[Spring] AOP Transaction Management
  • AccountDao’s outMoney opens a transaction T1 because it is a modification operation
  • AccountDao’s inMoney opens a transaction T2 because it is a modification operation
  • AccountService’s transfer has no transactions.
    • If no exceptions are thrown during the run, then both T1 and T2 are submitted normally and the data is correct
    • If an exception is thrown in the middle of two methods, T1 commits the transaction because the execution was successful, and T2 is not executed because the exception was thrown.
    • This can lead to data errors
  1. With Spring’s transaction management turned on
[Spring] AOP Transaction Management
  • The @Transactional annotation is added on the transfer, and a transaction on the method T
  • Transaction T1 of the outMoney method of AccountDao is added to transaction T of transfer
  • Transaction T2 of the inMoney method of AccountDao is added to transaction T of transfer
  • This ensures that they are in the same transaction, and when an exception occurs in the business layer, the entire transaction is rolled back to ensure data accuracy.
By analyzing the above example, we can get the following concepts.
  • Transaction manager: the party that initiates the transaction, usually refers to the business layer in Spring to open the transaction method.
  • Transaction Coordinator: join the transaction side, in Spring usually refers to the data layer methods, can also be business layer methods
Caution: The The current transaction management is based onDataSourceTransactionManagerandSqlSessionFactoryBeanThe same data source is used.

3. Spring transaction properties

In the previous section, we introduced two concepts, the administrator of the transaction and the co-manager of the transaction, for these two concepts to do what exactly, we will be used through the case to use the next. In addition to these two concepts, there are other related to the configuration of the transaction are what we are going to learn. In this section, we study three main partsTransaction ConfigurationTransfer operations supplementary logActs of dissemination of services

1. Transaction configuration

[Spring] AOP Transaction Management All of the above properties can be found in the@Transactionalannotation is set on the parameter.
  • readOnly: true read-only transactions, false read-write transactions, add, delete and change should be set to false, query set to true.
  • timeout: set the timeout in seconds, within how long the transaction is not submitted successfully, it will be rolled back automatically, -1 means do not set the timeout.
  • rollbackFor: Rolls back a transaction when the specified exception occurs.
  • noRollbackFor: does not roll back a transaction when the specified exception occurs.
    • Think:Exception transactions are automatically rolled back, something we’ve known before.
    • noRollbackFor is to set no rollback for the specified exception, which is easy to understand.
    • rollbackFor is to specify the rollback of the exception, for the exception transaction should not be rolled back, why do you need to specify?
      • One correction to this piece of knowledge is that not all exceptions roll back the transaction, for example, the following code does not roll back
        public interface AccountService {
            /**
             * :: Transfer operations
             * @param out outgoing
             * :: @param in transferring parties
             * @param money amount
             */
            // Configure the current interface method to have a transaction
            public void transfer(String out,String in ,Double money) throws IOException;
        }
        
        @Service
        public class AccountServiceImpl implements AccountService {
        
            @Autowired
            private AccountDao accountDao;
        	@Transactional
            public void transfer(String out,String in ,Double money) throws IOException{
                accountDao.outMoney(out,money);
                //int i = 1/0; //This exception transaction is rolled back.
                if(true){
                    throw new IOException(); //This exception transaction will not be rolled back.
                }
                accountDao.inMoney(in,money);
            }
        
        }
  • The reason for this problem is that Spring’s transactions will only perform a transaction on theError ExceptionandRuntimeException Exceptionand its subclasses for transaction recall, other types of exceptions are not rolled back, corresponding to IOException does not meet the above conditions so do not roll back the
    • In this case, you can use the rollbackFor property to set the IOException not to be rolled back.
      @Service
      public class AccountServiceImpl implements AccountService {
      
          @Autowired
          private AccountDao accountDao;
      	 @Transactional(rollbackFor = {IOException.class})
          public void transfer(String out,String in ,Double money) throws IOException{
              accountDao.outMoney(out,money);
              //int i = 1/0; //This exception transaction is rolled back.
              if(true){
                  throw new IOException(); //This exception transaction will not be rolled back.
              }
              accountDao.inMoney(in,money);
          }
      
      }
  • rollbackForClassName is the same as rollbackFor, except that the attribute is the full name of the exception class.
  • noRollbackForClassName is equivalent to noRollbackFor, except that the attribute is the full name of the class for which the exception was raised.
  • isolationSet the isolation level of the transaction
    • DEFAULT :Default isolation level, the database isolation level will be used.
    • READ_UNCOMMITTED : Read Uncommitted
    • READ_COMMITTED : Read Committed
    • REPEATABLE_READ : Repeat Reads
    • SERIALIZABLE: Serialized
After introducing the above attributes, there is one last transaction propagation behavior, and in order to explain the setting of this attribute, we need to complete the following case.

2. Transfer operations append log cases

1. Needs analysis
Add a new requirement to the previous transfer case and record the log after completing the transfer.
  • Requirements: to realize the transfer operation between any two accounts, and leave a trace in the database for each transfer operation.
  • Requirements miniaturization: account A decreases money, account B increases money, database logs are recorded
Based on the above business requirements, let’s analyze how to implement. ①: Add a log module based on the transfer operation case to realize logging in the database. ②: business layer transfer operations (transfer), call reduce money, add money and logging functions One thing to note is that the intended effect in our case is. Logging of transfer operations regardless of whether they are successful or not
2. Environmental preparation
The environment is based on the transfer environment to complete, so the preparation of the environment can be referred to theSteps for setting up the environment for 6.1.3, on the basis of which we continue to write down
Step 1: Create the log table
create table tbl_log(
   id int primary key auto_increment,
   info varchar(255),
   createDate datetime
)
Step 2: Add LogDao interface
public interface LogDao {
    @Insert("insert into tbl_log (info,createDate) values(#{info},now())")
    void log(String info);
}
Step 3: Add LogService interface and implementation class
public interface LogService {
    void log(String out, String in, Double money);
}
@Service
public class LogServiceImpl implements LogService {

    @Autowired
    private LogDao logDao;
	@Transactional
    public void log(String out,String in,Double money ) {
        logDao.log("Transfer operation from "+out+" to "+in+",amount: "+money);
    }
}
Step 4: Add logs to the transfer operations
public interface AccountService {
    /**
     * :: Transfer operations
     * @param out outgoing
     * :: @param in transferring parties
     * @param money amount
     */
    // Configure the current interface method to have a transaction
    public void transfer(String out,String in ,Double money)throws IOException ;
}
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;
    @Autowired
    private LogService logService;
	@Transactional
    public void transfer(String out,String in ,Double money) {
        try{
            accountDao.outMoney(out,money);
            accountDao.inMoney(in,money);
        }finally {
            logService.log(out,in,money);
        }
    }

}
Step 5: Run the program
  • When the program runs normally, the transfer in the tbl_account table is successful, and the logging in the tbl_log table is successful
  • When an exception occurs between transfer operations (int i = 1/0), the transfer fails and tbl_account is successfully rolled back, but no data is added to the tbl_log table.
  • This result is not what we want, what is the reason? How can we fix it?
  • Reason for failure:Logging and transfer operations are part of the same transaction, both successful and unsuccessful.
  • End result:Logs must be kept regardless of the success of the transfer operation.

3. Dissemination of services

[Spring] AOP Transaction Management Analysis of the above cases.
  • The log method, inMoney method, and outMoney method are all additions, deletions, and modifications, and have transactions T1, T2, and T3, respectively.
  • transfer also has transactions turned on because of the @Transactional annotation T
  • As we mentioned earlier Spring transactions add T1,T2,T3 to transaction T
  • So when the transfer fails, all transactions are rolled back, resulting in the logs not being recorded
  • This doesn’t match our needs, and at this point we wondered if we could make the log method a separate transaction?
To solve this problem, you need to use transaction propagation behavior, by which we mean. Transaction propagation behavior: the attitude of the transaction coordinator towards the handling of transactions carried by the transaction manager. How exactly to solve this requires the use of the previously unsaidpropagation attribute
1. Modify the logService to change the propagation behavior of the transaction
@Service
public class LogServiceImpl implements LogService {

    @Autowired
    private LogDao logDao;
	//propagation sets transaction properties: propagation behavior is set to require a new transaction for the current operation
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void log(String out,String in,Double money ) {
        logDao.log("Transfer operation from "+out+" to "+in+",amount: "+money);
    }
}
Once it’s run, it achieves the result we want, and logs whether the transfer is successful or not.
2. Optional values for transaction propagation behavior
[Spring] AOP Transaction Management For us to develop the actual use of words, because the default value needs to be the transaction is constant. According to the development process to choose other can be, for example, the case of the need for new transactions will need to be manually configured. In fact, there are transactions on the incoming and outgoing operations, using the default value.

Recommended Today

uniapp and applet set tabBar and show and hide tabBar

(1) Set the tabBar: uni.setTabberItem({}); wx.setTabberItem({}); indexnumberisWhich item of the tabBar, counting from the left, is indexed from 0.textstringnoButton text on tabiconPathstringnoImage PathselectedIconPathstringnoImage path when selectedpagePathstringnoPage absolute pathvisiblebooleannotab Whether to display uni.setTabBarItem({ index: 0, text: ‘text’, iconPath: ‘/path/to/iconPath’, selectedIconPath: ‘/path/to/selectedIconPath’, pagePath: ‘pages/home/home’ }) wx.setTabBarItem({ index: 0, text: ‘text’, iconPath: ‘/path/to/iconPath’, selectedIconPath: ‘/path/to/selectedIconPath’, pagePath: ‘pages/home/home’ }) […]