Spring transaction management core interface

Introduction to the

Spring’s transaction management provides three core interface: PlatformTransactionManager, TransactionDefinition, TransactionStatus. In addition to learning how to configure transaction management with Spring, we also need to learn the role of these three interfaces.

1. The PlatformTransactionManager transaction manager

Spring transaction strategy are implemented through the PlatformTransactionManager interface, the interface is the core of the Spring transaction strategy, the Spring framework does not directly manage transactions, but is entrusted to a third party affairs to implement the persistence framework.

package org.springframework.transaction;

public interface PlatformTransactionManager {
    // Use TransactionDefinition to get the "transaction state" to manage transactions
    TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException;
    / / submit
    void commit(TransactionStatus var1) throws TransactionException;
    / / rollback
    void rollback(TransactionStatus var1) throws TransactionException;
}
Copy the code

2.TransactionDefinition Defines transaction base attributes (rules)

Define the basic properties (rules) of a transaction. Spring defines five properties: isolation level, propagation behavior, read-only, transaction timeout, and rollback rules.

3.TransactionStatus TransactionStatus

It is used to record the state of transactions, and it is used to obtain or judge the state information of simple transactions.

Spring uses transaction management

Transactions use transactions in Spring in two ways: programmatic and declarative. At present, the mainstream use is declarative transactions, simple and easy to use, programming transactions in the actual development of almost no use.

Programmatic transaction

Allows users to define transaction boundaries precisely in code. This is similar to JDBC programming to implement transaction management. Management using PlatformTransactionManager TransactionTemplate or directly use the bottom. For programmatic transaction management, Spring recommends using TransactionTemplate.

Use TransactionTemplate to manage transactions manually and write some of the rules for the transaction yourself. Relatively troublesome.

Declarative transaction

While learning AOP, AOP can be used for transaction management, so transaction management is implemented using AOP. Intercepts before and after a method, creates or joins a transaction before the method starts, and commits or rolls back after execution. The advantage over programmatic transactions is that transactions are not managed programmatically, so there is no need to involve transaction management in the business logic, just need to declare the relevant rules in the configuration file (or annotations), and the transaction rules can be applied in the business logic.

SpringTX case

The following is the implementation of Spring integration Mybatis written simple transaction management transfer system

DaoMapper source code:

public interface DaoMapper {
    / / roll-out
    @Update("update user set balance=balance-#{money} where id=#{id}")
    void push(@Param("id") int id, @Param("money") double money);
    / / turn to
    @Update("update user set balance=balance+#{money} where id=#{id}")
    void pull(@Param("id")int id, @Param("money")double money);
}
Copy the code

UserServerImpl source code:

@Service
public class UserServerImpl implements UserServer {
    private DaoMapper mapper;

    @Autowired
    public void setMapper(DaoMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public void transferAccounts(int pushId, double money, int pullId) { mapper.push(pushId,money); mapper.pull(pullId,money); }}Copy the code

The Test source code:

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserServer server = (UserServer) context.getBean("userServerImpl");
        server.transferAccounts(1.100.2); }}Copy the code

Spring configuration file with no transaction management configured:


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       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-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <! -- Scan with annotations -->
    <context:component-scan base-package="com.lyl.*"/>
    <! Jdbc.properties file -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <! Configure the data source bean.
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${m_driver}"/>
        <property name="url" value="${m_url}"/>
        <property name="username" value="${m_user}"/>
        <property name="password" value="${m_password}"/>
    </bean>
    <! Mybatis -->
    <! SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <! -- Scan Mapper -->
    <bean id="mapper" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.lyl.dao"/>
    </bean>
Copy the code

Comparison of test results before running:After the operation:UserServerImpl business code:

@Service
public class UserServerImpl implements UserServer {
    private DaoMapper mapper;

    @Autowired
    public void setMapper(DaoMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public void transferAccounts(int pushId, double money, int pullId) {
        mapper.push(pushId,money);
        // Abort the program with an error
        int i=7/0; mapper.pull(pullId,money); }}Copy the code

Found after running:This indicates that we did not add transaction management for this group of SQL operations, and the SQL operations that have been executed after the error report are directly submitted, which results in a different result from our ideal. We hope that after the error report, no one will add money, no one will deduct money. This is where transaction management comes in.

Join transaction management (using declarative transactions) :


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       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-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <! -- Scan with annotations -->
    <context:component-scan base-package="com.lyl.*"/>
    <! Jdbc.properties file -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <! Configure the data source bean.
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${m_driver}"/>
        <property name="url" value="${m_url}"/>
        <property name="username" value="${m_user}"/>
        <property name="password" value="${m_password}"/>
    </bean>
    <! Mybatis -->
    <! SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <! -- Scan Mapper -->
    <bean id="mapper" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.lyl.dao"/>
    </bean>
    <! -- Configure JDBC transaction manager -->
    <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <! Configure the transaction aspect Bean -->
    <tx:advice id="txManager" transaction-manager="tx">
        <tx:attributes>
            <! -- name: cut point * wildcard to be woven -->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <! Configure AOP -->
    <aop:config >
        <aop:pointcut id="put" expression="execution(* com.lyl.server.impl.UserServerImpl.transferAccounts(..) )"/>
        <aop:advisor advice-ref="txManager" pointcut-ref="put"/>
    </aop:config>
</beans>
Copy the code

Run the program:Even if the program reported an error, we did not find the above situation – just deduct money without adding money, in fact, the transaction helps us manage the SQL operation, error, rollback, corresponding to our atomicity.

Propagation behavior of transactions

When a transaction method is called by another transaction method, you must specify how the transaction should propagate. There are seven propagating behaviors for transactions in Spring. Here’s a brief introduction to two common ones, and you can search for the others.

Definition of propagation properties

  • Annotated definition

  • Profile Definition

REQUIRED: Join a transaction if it currently exists, or create a new one if it does not.

The simple idea is that if a method is called by another method, the caller joins with a transaction and creates a transaction without one. They both use the same thing.

REQUIRES_NEW: If a transaction currently exists, suspend it and create a new one.

To put it simply: they are not using the same transaction. If the current transaction (A) is suspended, then any database operations on the new transaction (B) are no longer under A, but under B until method B returns, and subsequent code in A continues to execute. These operations are committed or rolled back under A.

These two transactions are most commonly used to distinguish whether they are in the same transaction.

The isolation level of the transaction

  1. ISOLATION_DEFAULT: this is a PlatfromTransactionManager default isolation level, using the database to the default transaction isolation level.
The other four correspond to JDBC isolation levelsCopy the code
  1. ISOLATION_READ_UNCOMMITTED: This is the lowest isolation level for a transaction, allowing another transaction to see the uncommitted data of this transaction.
This isolation level produces dirty reads, non-repeatable reads, and phantom reads.Copy the code
  1. ISOLATION_READ_COMMITTED: Ensures that data modified by one transaction is committed before it can be read by another transaction. Another transaction cannot read uncommitted data from that transaction
  2. ISOLATION_REPEATABLE_READ: This transaction isolation level prevents dirty, non-repeatable reads. But illusionary reading can occur.
In addition to ensuring that one transaction cannot read uncommitted data from another transaction, it also ensures that the following situation (unrepeatable reads) is avoided.Copy the code
  1. ISOLATION_SERIALIZABLE This is the most expensive but reliable transaction isolation level. Transactions are processed for sequential execution.
In addition to preventing dirty read, not repeat read, but also avoid phantom read.Copy the code

What are dirty data, dirty reads, unrepeatable reads, and phantom reads?

Dirty read: When a transaction is accessing data and making changes to the data that have not been committed to the database, another transaction also accesses the data and then consumes the data. Because this data is not committed yet, another transaction reads this data as dirty data, and it may not be correct to act on dirty data.

Non-repeatable read: The same data is read multiple times in a transaction. The same data is accessed by another transaction while the transaction is still active.

Then, between the two reads in the first transaction, the data read by the first transaction may not be the same because of the modifications made by the second transaction. This happens when the data read twice in a transaction is not the same and is therefore called a non-repeatable read.Copy the code

Phantom read: a phenomenon that occurs when a transaction is not executed independently, for example, when the first transaction modifies data in a table

All rows to the table. At the same time, the second transaction also modifies the data in the table by inserting a new row into the table. Then, as if in an illusion, the user operating on the first transaction will discover that there are unmodified rows in the table.Copy the code