1. Basic Concepts

  • In the application field of JavaEE enterprise development, in order to ensure the integrity and consistency of data, the concept of database transaction must be introduced
  • A transaction is a set of database operations that are logically closely related and combined into a single entity (unit of work), all or none of which are executed.
  • Four Characteristics of Transactions (ACID)
    • Atomicity: the meaning of “atom” is “indivisible”, either all or none.
    • Consistency: “Consistency” refers to the consistency of data. The consistency principle requires that no matter how many operations are involved in a transaction, the data must be correct before and after the transaction. If one or more operations fail during a transaction, the data must be restored to the state before the transaction was executed, which is called rollback.
    • Isolation: During the actual operation of an application, transactions tend to execute concurrently, so it is likely that many transactions will process the same data at the same time, so each transaction should be isolated from others to prevent data corruption. The principle of isolation requires that multiple transactions execute concurrently without interfering with each other.
    • Durability: Durability requires that modifications to data persist permanently after transactions complete. Normally, changes made by transactions to data should be written to persistent storage.

Second, Spring transaction management

2.1 Programmatic transactions

  • Use the native JDBC API for transaction management
    • Gets the database Connection object
    • Cancel the automatic commit of the transaction
    • ] Perform an operation
    • Manually commit the transaction when the operation completes normally
    • Rollback transaction on execution failure
    • Close related resources
  • Evaluation: Transaction management using the native JDBC API is the cornerstone of all transaction management approaches and is the most typical of programmatic transaction management. Compared with core business, transaction management code is obviously non-core business. If multiple modules use the same pattern of code for transaction management, it will obviously cause a large degree of code redundancy.

2.2 Declarative transactions

  • Declarative transactions are in most cases better than programmatic transaction management: it separates transaction management code from business methods and implements transaction management declaratively.
  • The fixed pattern of transaction management code, as a crosscutting concern, can be modularized through an AOP approach to declarative transaction management with the help of the Spring AOP framework.
  • Spring defines an abstraction layer on top of the different transaction management apis, which can be configured to enable application developers to use Spring’s transaction management mechanism without having to understand the underlying implementation details of the transaction management API.
  • Spring supports both programmatic and declarative transaction management

2.3 Transaction manager

  • Spring’s core transaction management abstraction isPlatformTransactionManager. It encapsulates a set of technology-independent methods for transaction management. Regardless of which of Spring’s transaction management strategies (programmatic or declarative) are used, a transaction manager is required.
  • The main implementation of the transaction manager
    • DataSourceTransactionManager: Only one data source needs to be processed in the application and accessed via JDBC.
    • JtaTransactionManager: Uses JTA(Java Transaction API) for Transaction management on JavaEE application servers
    • HibernateTransactionManager: Use Hibernate framework to access database

Transaction operation

  • Transactions are generally added to the Service layer (business logic layer) within the JavaEE three-tier structure.

3.1 Annotation-based Configuration

3.1.1 database

  • Set up a database
    USE spring;
    DROP TABLE IF EXISTS account;
    CREATE TABLE account(
    	id 	INT 		PRIMARY KEY AUTO_INCREMENT,
    	`name`	VARCHAR(30) 	DEFAULT NULL,
    	money 	DECIMAL		NOT NULL
    );
    
    INSERT INTO 
    account(`name`, money)
    VALUES
    ("Mary", 1000),
    ("Jerry", 1000);
    Copy the code

3.1.2 Project Catalog

  • File directory

3.1.3 Configuration File

  • Modify the namespace of the Spring configuration file by adding the following
    <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:tx="http://www.springframework.org/schema/tx"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd ">
    Copy the code
  • Enable component annotation scanning
    <context:component-scan base-package="com.du.spring"/>
    Copy the code
  • Instantiate the database connection pool
        <! -- Instantiate database connection pool -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url">
            <value><! [CDATA[jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&rewriteBatchedState ments=true]]></value>
        </property>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    Copy the code
  • Instantiation JdbcTemplate
    <! -- Instantiate JdbcTemplate-->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    Copy the code
  • Configure the transaction manager
    <! -- Configure transaction manager -->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    Copy the code
  • Open transaction annotations
    <! -- Enable transaction annotations -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    Copy the code

3.1.4 Java file

  • Create the corresponding Java Bean
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Account {
      private int id;
      String name;
      BigDecimal money;
    }
    Copy the code
  • DAO layer
    public interface AccountDao {
      /** * change the account balance, positive to transfer, negative to transfer out *@param id
       * @param money
       */
      int changeAccount(int id, BigDecimal money);
    }
    Copy the code
    @Repository
    public class AccountDaoImpl implements AccountDao {
      JdbcTemplate jdbcTemplate; // Automatic injection
    
      @Override
      public int changeAccount(int id, BigDecimal money) {
        String sql = "update account set money = money + ? where id = ?";
        return jdbcTemplate.update(sql, money, id);
      }
    
      @Autowired
      public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate; }}Copy the code
  • The Service layer.@Transactional You can annotate both methods and classes.
    @Service
    @Transactional  // Transaction annotations. All methods under this class follow transactions
    public class AccountService{
      AccountDao accountDao;
    
      public int transfer(int fromId, int toId, BigDecimal money) {
        int res1 = accountDao.changeAccount(fromId, money.negate());
        //System.out.println(10 / 0);
        int res2 = accountDao.changeAccount(toId, money);
        if (res1 > 0 && res2 > 0)
          return 1;
        else
          return 0;
      }
    
      @Autowired
      public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao; }}Copy the code

    Note: If the class has an interface, transactional AOP will use JDK proxies and then an error will be reported.. Let AOP use Cglib only with classes that have no interfaces

  • test
    @Test
      public void transferTest(a) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        AccountService accountService = context.getBean("accountService", AccountService.class);
        int res = accountService.transfer(1.2.new BigDecimal("100"));
        if (res > 0)
          System.out.println("Transfer successful");
        else
          System.out.println("Transfer failed");
      }
    Copy the code

Transactional adds transaction annotations

  • @TransactionalCan be added to a class or a method
  • If you add this annotation to a class, all methods in the class add transactions
  • If you add this annotation to the method, add a transaction to the method
  • In this annotation, you can add some parameters

4.1 Transaction Propagation behavior (Propagation Attributes)

  • propagation: transaction propagation behavior (i.e., transaction nesting)

  • Commonly used areREQUIREDandREQUIRED_NEW.
    • Be labeledREQUIREDIf a big transaction has an open transaction, it will “sit in the same boat” with it, and if the middle overturned, it will overturn together, neither of which will take effect.
    • And if it’s labeled asREQUIRED_NEW, is equivalent to their own boat, as long as they do not overturn, complete the task, it is good to immediately submit, big business overturned, do not affect their own. (However, if the car in front of you turns over before it’s your turn to drive, you can’t drive, or you can’t.) (If they overturned, big business can not continue to execute, will also follow the overturned)

  • The first one in the figure above is small transactions all of themREQUIREDYes, the second is all the little thingsREQUIRED_NEW.
  • Note that large transactions cannot call small transactions under the same class, otherwise it will be a simple method call. (Since transactions are based on AOP, and AOP is executed after the proxy object, you need to get the proxy object of the class to which the small transaction belongs outside the class and call it again for the transaction to execute properly.)

4.2 Isolation Level (Isolation attribute)

  • isolation: Transaction isolation level. Isolation needs to be considered between multiple transactions, otherwise some problems can occur.
  • Dirty read: An uncommitted transaction reads data from another uncommitted transaction, such as:
    • Transaction01Change the AGE value of a record from 20 to 30.
    • Transaction02Read the updated value of Transaction01:30.
    • Transaction01The AGE value is restored to 20.
    • Transaction02Reading 30 is an invalid value.
  • Non-repeatable read: an uncommitted transaction reads data modified by another committed transaction, such as:
    • Transaction01The AGE value is 20.
    • Transaction02Change the AGE value to 30.
    • Transaction01The AGE value of the second read is 30, which is inconsistent with the first read.
  • Virtual read: an uncommitted transaction reads data from another committed transaction
    • Transaction01Part of the data in the STUDENT table is read.
    • Transaction02Insert a new row into the STUDENT table.
    • Transaction01There are some extra rows when the STUDENT table is read.
  • Solution: Set the isolation level
  • Read unsubmitted:READ UNCOMMITTEDTo allowTransaction01readTransaction02Uncommitted changes.
  • Read submitted:READ COMMITTED,Transaction01Can only readTransaction02Committed changes.
  • Repeatable reading:REPEATABLE READTo ensure thatTransaction01The same value can be read from a field multiple times, i.eTransaction01During executionDisallow other transactions from updating this field.
  • Serialization:SERIALIZABLETo ensure thatTransaction01It is possible to read the same row from a table multiple times, inTransaction01During the execution,Prohibit other transactions from adding, updating, or deleting this table. Can avoid any concurrency problems, but performance is very poor.

4.3 Timeout (Timeout property)

  • timeout: The transaction needs to commit within a certain amount of time. If it does not commit, it will be rolled back. (Number entered in seconds)

4.4 Read-only (Read-only attribute)

  • readOnly(): Indicates whether it is read-only. The default value is false, which indicates that you can query or add or modify operations. If this parameter is set to true, only query can be performed.
  • Set toread-onlyThe efficiency is higher. However, do not modify it. Otherwise, exceptions may occur.

4.5.. The rollback

  • Default transactions only encounter runtime exceptionsRuntimeExceptionorErrorIs rolled back, while compile-time exceptions caught are not rolled back.
  • You can do that by settingrollbackForTo add some compile-time exceptions to the rollback trigger column.
  • Set up thenoRollbackForTo remove some runtime exceptions from the rollback list.

Declarative transaction configuration based on XML documents

<! -- Configure transaction facets -->
	<aop:config>
		<aop:pointcut 
			expression="execution(* com.atguigu.tx.component.service.BookShopServiceImpl.purchase(..) )" 
			id="txPointCut"/>
		<! Associate pointcut expressions with transaction property configurations
		<aop:advisor advice-ref="myTx" pointcut-ref="txPointCut"/>
	</aop:config>
	
	<! Configure xmL-based declarative transactions -->
	<tx:advice id="myTx" transaction-manager="transactionManager">
		<tx:attributes>
			<! Set transaction properties for a specific method -->
			<tx:method name="find*" read-only="true"/>
			<tx:method name="get*" read-only="true"/>
			<tx:method name="purchase" 
				isolation="READ_COMMITTED" 
	no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException"
				propagation="REQUIRES_NEW"
				read-only="false"
				timeout="10"/>
		</tx:attributes>
	</tx:advice>
Copy the code
  • The default database connection pool and transaction manager are registered. Now you need to configure the aspect, pointcut, and transaction manager.
  • First, as with normal AOP, you need to be in<aop:config>To configure AOP. through<aop:pointcut>Set the pointcut. However, unlike the usual setting of the aspect, you do not need to set the aspect, but you need to set transaction enhancement<aop:advisor>.
  • through<tx:advice>To set the details of transaction enhancement. The transaction manager needs to be bound. in<tx:attributes>In, select the method that needs to use transactions among all pointcuts, and you can set some properties in it that are also available in annotations.

Complete annotation development

  • The content is the same as the XML configuration, except that the content is configured through annotations
    @Configuration
    @ComponentScan(basePackages = "com.du.spring") // Component scan
    @EnableTransactionManagement  // Start transaction manager
    public class TxConfig {
      // Instantiate the bean and return
      @Bean
      public DruidDataSource getDruidDataSource(a) {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring? useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&rewriteBatchedStatements=true");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
      }
    
      @Bean
      public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource) {  // Place the object to be configured in the parameter and autowired into it
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
      }
    
      @Bean
      public DataSourceTransactionManager getDataSourceTransactionManager(DruidDataSource dataSource) {
        DataSourceTransactionManager manager = new DataSourceTransactionManager();
        manager.setDataSource(dataSource);
        returnmanager; }}Copy the code