Spring【Spring transaction (transaction introduction, Spring transaction management scheme, Spring transaction manager, control API, related configuration)】(7)-Comprehensive and detailed explanation (learning summary --- from entry to deepening)

 

Table of contents

Spring transaction_transaction introduction

Spring Transaction_Spring Transaction Management Solution 

Spring Transaction_Spring Transaction Manager

Spring transaction_transaction control API

Related configuration of Spring transaction_transaction 

 Spring transaction _ transaction propagation behavior

Spring transaction_transaction isolation level 

        Spring transaction_annotation configuration declarative transaction


 

 Spring transaction_transaction introduction

 Transactions: Indivisible atomic operations. That is, a series of operations either succeed at the same time or fail at the same time.

During the development process, transaction management is generally in the service layer, and the database may be operated multiple times in the service layer, and these operations are inseparable. Otherwise, when the program reports an error, data exceptions may occur.

For example: when Zhang San transfers money to Li Si, the database needs to be operated twice: Zhang San's deposit decreases and Li Si's deposit increases. If an exception occurs between these two database operations, data errors will result.

1. Prepare the database

CREATE DATABASE `spring` ;
USE `spring`;
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `balance` double DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT
CHARSET=utf8;
insert  into `account`(`id`,`username`,`balance`) values (1,'张三',1000),(2,'李四',1000);

2. Create a maven project and introduce dependencies

<dependencies>
    <!-- mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
   <!-- mysql驱动包 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connectorjava</artifactId>
        <version>8.0.26</version>
    </dependency>
    <!-- druid连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.8</version>
    </dependency>
    <!-- spring -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>springcontext</artifactId>
        <version>5.3.13</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.3.13</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>springjdbc</artifactId>
        <version>5.3.13</version>
    </dependency>
    <!-- MyBatis与Spring的整合包,该包可以让Spring创建MyBatis的对象 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatisspring</artifactId>
        <version>2.0.6</version>
    </dependency>
    <!-- junit,如果Spring5整合junit,则junit版本至少在4.12以上 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!-- spring整合测试模块 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>springtest</artifactId>
        <version>5.3.13</version>
        <scope>test</scope>
    </dependency>
</dependencies>

3. Create a configuration file

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      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">
    <!-- 包扫描 -->
    <context:component-scan basepackage="com.tong"></context:component-scan>
    <!-- 创建druid数据源对象 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql:///spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.tong.dao"></property>
    </bean>
</beans>

4. Write Java code

// 账户
public class Account {
    private int id; // 账号
    private String username; // 用户名
    private double balance; // 余额
    
    // 省略getter/setter/tostring/构造方法
}

@Repository
public interface AccountDao {
    // 根据id查找用户
    @Select("select * from account where id = #{id}")
    Account findById(int id);
    // 修改用户
    @Update("update account set balance = #{balance} where id = #{id}")
    void update(Account account);
}

@Service
public class AccountService {
    @Autowired
    private AccountDao accountDao;
    /**
     * 转账
     * @param id1 转出人id
     * @param id2 转入人id
     * @param price 金额
     */
    public void transfer(int id1, int id2, double price) {
        // 转出人减少余额
        Account account1 = accountDao.findById(id1);
        account1.setBalance(account1.getBalance()-price);
        accountDao.update(account1);
        int i = 1/0; // 模拟程序出错
        // 转入人增加余额
        Account account2 = accountDao.findById(id2);
        account2.setBalance(account2.getBalance()+price);
        accountDao.update(account2);
   }
}

5. Test transfer

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath :applicationContext.xml")
public class AccountServiceTest {
    @Autowired
    private AccountService accountService;
    @Test
    public void testTransfer(){
        accountService.transfer(1,2,500);
   }
}

At this time, if there is no transaction management, Zhang San's balance will decrease, but Li Si's balance will not increase. Therefore, transaction processing is located in the business layer, that is, a service method cannot be separated.

Spring Transaction_Spring Transaction Management Solution 

 Manually adding transactions at the service layer can solve this problem:

@Autowired
private SqlSessionTemplate sqlSession;
public void transfer(int id1, int id2,double price) {
    try{
        // account1修改余额
        Account account1 = accountDao.findById(id1);
        account1.setBalance(account1.getBalance()- price);
        accountDao.update(account1);
        int i = 1/0; // 模拟转账出错
        // account2修改余额
        Account account2 = accountDao.findById(id2);
        account2.setBalance(account2.getBalance()+price);
        accountDao.update(account2);  
        sqlSession.commit();
     }catch(Exception ex){
        sqlSession.rollback();
   }
}

But manual commit and rollback of transactions is not allowed under Spring management. At this point we need to use Spring's transaction management solution, which provides two transaction management solutions in the Spring framework:

1 Programmatic transaction : realize transaction management by writing code.

2 Declarative transaction : Realize transaction management based on AOP technology.

In the Spring framework, programmatic transaction management is rarely used, and we will study declarative transaction management in detail. Spring's declarative transaction management adopts AOP technology at the bottom layer. Its biggest advantage is that it does not need to manage transactions through programming. It only needs to declare relevant rules in the configuration file to apply transaction rules to business logic. 

 Use AOP technology to add the following notification to the service method:

 

Spring Transaction_Spring Transaction Manager

 Spring relies on the transaction manager for transaction management. The transaction manager is a notification class. We set the cut point for the notification class as a service layer method to complete automatic transaction management. Since different technologies operate databases, the methods for performing transaction operations are different. For example: JDBC commits transaction is connection.commit(), MyBatis commits transaction is sqlSession.commit(), so Spring provides multiple transaction managers.

 We use MyBatis to operate the database, and then use DataSourceTransactionManager for transaction management.

 1. Introduce dependencies

<!-- 事务管理 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.3.13</version>
</dependency>
<!-- AspectJ -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
</dependency>

2. Introduce constraints in the configuration file

xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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

3. Perform transaction configuration

<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 进行事务相关配置 -->
<tx:advice id = "txAdvice">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<!-- 配置切面 -->
<aop:config>
    <!-- 配置切点 -->
    <aop:pointcut id="pointcut" expression="execution(* com.tong.service.*.*(..))"/>
    <!-- 配置通知 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"></aop:advisor>
</aop:config>

Spring transaction_transaction control API

 Spring's transaction control function is provided by three interfaces. These three interfaces are implemented by Spring. We rarely use them in development. We only need to understand their functions:

PlatformTransactionManager interface

PlatformTransactionManager is a transaction manager interface provided by Spring, and all transaction managers implement this interface. This interface provides three transaction operation methods:

1. TransactionStatus getTransaction (TransactionDefinition definition): Get transaction status information.

2. void commit (TransactionStatus status): transaction commit

3. void rollback (TransactionStatus status): transaction rollback

 TransactionDefinition interface

TransactionDefinition is the definition information object of the transaction, which has the following methods:

String getName(): Get the transaction object name.

int getIsolationLevel(): Get the isolation level of the transaction.

int getPropagationBehavior(): Get the propagation behavior of the transaction.

int getTimeout(): Get the timeout time of the transaction.

boolean isReadOnly(): Gets whether the transaction is read-only.

TransactionStatus interface 

TransactionStatus is the status interface of the transaction, which describes the status information of the transaction at a certain point in time. It has the following methods:

void flush() refresh transaction

boolean hasSavepoint() Gets whether there is a savepoint

boolean isCompleted() Get whether the transaction is completed

boolean isNewTransaction() Get whether it is a new transaction

boolean isRollbackOnly() Get whether to roll back

void setRollbackOnly() Set transaction rollback

Related configuration of Spring transaction_transaction 

Transaction related configuration can be done in <tx:advice>:

<tx:advice id="txAdvice">
    <tx:attributes>
        <tx:method name="*"/>
        <tx:method name="find*" readonly="true"/>
    </tx:attributes>
</tx:advice>

 Spring transaction _ transaction propagation behavior

 Transaction propagation behavior refers to how transactions are propagated among methods when multiple methods containing transactions call each other. If other service methods are called in the method of the service layer, assuming that the transaction must be started every time the service method is executed, then there is no guarantee that the outer method and the inner method are in the same transaction.

// method1的所有方法在同一个事务中
public void method1(){
    // 此时会开启一个新事务,这就无法保证method1()中所有的代码是在同一个事务中
    method2();
    System.out.println("method1");
}
public void method2(){
    System.out.println("method2");
}

The propagation feature of the transaction is to solve this problem. Spring helps us put the outer method and the inner method into the same transaction.

Spring transaction_transaction isolation level 

 The transaction isolation level reflects the processing attitude when the transaction is submitted for concurrent access. The higher the isolation level, the lower the possibility of data problems, but the lower the efficiency.

 Spring transaction_annotation configuration declarative transaction

Spring supports the configuration of declarative transactions using annotations. The usage is as follows:

1. Registration transaction annotation driver

<!-- 注册事务注解驱动 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

2. Add @Transactional to the method or class that needs transaction support

 @Service
// 作用于类上时,该类的所有public方法将都具有该类型的事务属性
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
public class AccountService {
    @Autowired
    private AccountDao accountDao;
    /**
     * 转账
     * @param id1 转出人id
     * @param id2 转入人id
     * @param price 金额
     */
    // 作用于方法上时,该方法将都具有该类型的事务属性
    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
    public void transfer(int id1, int id2,double price) {
        // account1修改余额
        Account account1 = accountDao.findById(id1);
        account1.setBalance(account1.getBalance()-price);
        accountDao.update(account1);
        int i = 1/0; // 模拟转账出错
        // account2修改余额
        Account account2 = accountDao.findById(id2);
        account2.setBalance(account2.getBalance()+price);
        accountDao.update(account2);
   }
}

3 The configuration class replaces the annotation transaction support in xml: write @EnableTranscationManagement above the configuration class

@Configuration
@ComponentScan("com.tong")
@EnableTransactionManagement
public class SpringConfig {
    @Bean
    public DataSource getDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
       druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
       druidDataSource.setUrl("jdbc:mysql:///spring");
       druidDataSource.setUsername("root");
       druidDataSource.setPassword("root");
        return druidDataSource;
}
    @Bean
    public SqlSessionFactoryBean getSqlSession(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
   }
    @Bean
    public MapperScannerConfigurer getMapperScanner(){
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.tong.dao");
        return mapperScannerConfigurer;
   }
    @Bean
    public DataSourceTransactionManager getTransactionManager(DataSource dataSource){
         DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
      dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
   }
}

Guess you like

Origin blog.csdn.net/m0_58719994/article/details/131744474