2021-09-26 Spring5 declarative transaction control

Keywords: Java Hibernate Spring

Spring 5 declarative transaction control

1. Programming transaction control related objects

1.1 PlatformTransactionManager

The PlatformTransactionManager interface is the transaction manager of spring, which provides our common methods of operating transactions.

methodexplain
TransactionStatus getTransaction(TransactionDefination defination)Gets the status information of the transaction
void commit(TransactionStatus status)Commit transaction
void rollback(TransactionStatus status)Rollback transaction

be careful:
PlatformTransactionManager is an interface type. Different Dao layer technologies have different implementation classes, such as:

When Dao layer technology is JDBC or mybatis: org.springframework.jdbc.datasource.DataSourceTransactionManager
When Dao layer technology is Hibernate: org.springframework.orm.hibernate5.hibernatetransaction Manager

1.2 TransactionDefinition

TransactionDefinition is the transaction definition attribute information object, which contains the following methods:

methodexplain
int getIsolationLevel()Gets the isolation level of the transaction
int getPropogationBehavior()Get propagation behavior of transaction
int getTimeout()Get timeout
boolean isReadOnly()Read only
  1. Transaction isolation level
    Setting the isolation level can solve the problems caused by transaction concurrency, such as dirty reading, non repeatable reading and virtual reading (phantom reading).
    ISOLATION_DEFAULT
    ISOLATION_READ_UNCOMMITTED
    ISOLATION_READ_COMMITTED
    ISOLATION_REPEATABLE_READ
    ISOLATION_SERIALIZABLE

  2. Transaction propagation behavior

    REQUIRED: if there is no transaction currently, create a new transaction. If there is already a transaction, join it. General selection (default)
    SUPPORTS: SUPPORTS the current transaction. If there is no transaction, it will be executed in a non transactional manner (no transaction)
    MANDATORY: use the current transaction. If there is no transaction, an exception will be thrown
    REQUERS_NEW: create a new transaction. If it is currently in a transaction, suspend the current transaction.
    NOT_SUPPORTED: perform operations in a non transactional manner. If there is a transaction, suspend the current transaction
    NEVER: runs in a non transactional manner. If there is a transaction, an exception will be thrown
    NESTED: if a transaction currently exists, it is executed within a NESTED transaction. If there is no current transaction, perform an operation similar to REQUIRED
    Timeout: the default value is - 1. There is no timeout limit. If yes, set in seconds
    Read only: it is recommended to set it as read-only when querying

1.3 TransactionStatus

The TransactionStatus interface provides the specific running status of transactions. The methods are described below.

methodexplain
boolean hasSavepoint()Store rollback points
boolean isCompleted()Is the transaction complete
boolean isNewTransaction()Is this a new transaction
boolean isRollbackOnly()Whether the transaction is rolled back

1.4 key points of knowledge

Three objects of programming transaction control
PlatformTransactionManager
TransactionDefinition
TransactionStatus

2. Declarative transaction control based on XML

2.1 what is declarative transaction control

Spring's declarative transaction, as its name implies, is to handle transactions in a declarative manner. The declaration here refers to the declaration in the configuration file, and the declarative transaction in the spring configuration file is used to replace the code transaction.

The role of declarative transactions

  • Transaction management does not invade developed components. Specifically, the business logic object will not realize that it is in the process of transaction management. In fact, it should be so, because transaction management is a system level service, not a part of business logic. If you want to change the transaction management plan, you only need to reconfigure it in the definition file. When transaction management is not needed, As long as you modify the setting file, you can remove the transaction management service without changing the code and recompiling, which is very convenient for maintenance

Note: the underlying layer of Spring declarative transaction control is AOP.

  • Business logic pointcut
  • Transaction control notification

2.2 implementation of declarative transaction control (quick start)

applicationContext.xml

Configuration points:

Platform transaction manager configuration
Configuration of transaction notifications
Configuration of transaction aop weaving

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        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
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
">
    <context:property-placeholder location="jdbc.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>


    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <bean id="accountDao" class="com.raphuscucullatus.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

    <!--  Target objects and business methods are pointcuts  -->
    <bean id="accountService" class="com.raphuscucullatus.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!--  Configure the platform transaction manager to provide notifications   -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--  notice  -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!--  Configuration weaving  -->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.raphuscucullatus.service.impl.*.*(..))"/>
    </aop:config>
</beans>

jdbc.priperties

jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=088.5741

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.raphuscucullatus</groupId>
    <artifactId>spring_tx</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

    </dependencies>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>

dao

package com.raphuscucullatus.dao.impl;

import com.raphuscucullatus.dao.AccountDao;
import org.springframework.jdbc.core.JdbcTemplate;

/**
 * @author raphus cucullatus
 * @version 2021/9/25 23:28
 * @since JDK8
 */
public class AccountDaoImpl implements AccountDao {

    private JdbcTemplate jdbcTemplate;
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
        this.jdbcTemplate = jdbcTemplate;
    }
    @Override
    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set money=money-? where name = ?",money,outMan);
    }

    @Override
    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money=money+? where name = ?",money,inMan);
    }
}

service

package com.raphuscucullatus.service.impl;

import com.raphuscucullatus.dao.AccountDao;
import com.raphuscucullatus.service.AccountService;

/**
 * @author raphus cucullatus
 * @version 2021/9/25 23:26
 * @since JDK8
 */
public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan,money);
        System.out.println(1/0);
        accountDao.in(inMan,money);
    }
}

test case

package com.raphuscucullatus.controller;

import com.raphuscucullatus.service.AccountService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author raphus cucullatus
 * @version 2021/9/25 23:30
 * @since JDK8
 */
public class AccountController {

    private AccountService accountService;

    public void setAccountService(AccountService accountService){
        this.accountService=accountService;
    }

    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = classPathXmlApplicationContext.getBean(AccountService.class);
        accountService.transfer("tom","jerry",1000);
    }
}

2.3 XML explanation

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        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
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
">
    <context:property-placeholder location="jdbc.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="accountDao" class="com.raphuscucullatus.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

    <!--  Target objects and business methods are pointcuts  -->
    <bean id="accountService" class="com.raphuscucullatus.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!--
      Configure platform transaction manager(The specific implementation class is provided by DAO Layer technology implementation decision)
        Used to provide notifications
    -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--
      advice notice
        attributes Set transaction properties
            name Tangent point method name
            isolation Isolation level
            propagation Communication behavior
            timeout Timeout
            read-only read-only
            o-rollback-for
            rollback-for
    -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
            <tx:method name="save" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
            <tx:method name="findAll" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/>
            <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!--
      Configuration weaving
        advice-ref Transaction reference
        pointcut breakthrough point 
    -->
    <aop:config>
        <!--    Extract tangent point    -->
<!--        <aop:pointcut id="transfer" expression="execution(* com.raphuscucullatus.service.impl.*.*(..))"/>-->
<!--        <aop:advisor advice-ref="txAdvice" pointcut-ref="transfer"/>-->
        <!--    Do not extract tangent points    -->
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.raphuscucullatus.service.impl.*.*(..))"/>
    </aop:config>
</beans>

3. Annotation based declarative transaction control

3.1 configuring declarative transaction control using annotations

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        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
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
">
    <context:property-placeholder location="jdbc.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

	<!--  Component scan(Annotation configuration)  -->
    <context:component-scan base-package="com.raphuscucullatus"/>

    <!--
      Configure platform transaction manager(The specific implementation class is provided by DAO Layer technology implementation decision)
        Used to provide notifications
    -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--  Annotation driven(Annotation configuration)  -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
  
</beans>

dao

package com.raphuscucullatus.dao.impl;

import com.raphuscucullatus.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

/**
 * @author raphus cucullatus
 * @version 2021/9/25 23:28
 * @since JDK8
 */
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set money=money-? where name = ?",money,outMan);
    }

    @Override
    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money=money+? where name = ?",money,inMan);
    }
}

service

package com.raphuscucullatus.service.impl;

import com.raphuscucullatus.dao.AccountDao;
import com.raphuscucullatus.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author raphus cucullatus
 * @version 2021/9/25 23:26
 * @since JDK8
 */
@Service("accountService")
public class AccountServiceImpl implements AccountService {

    /**
     *
     */
    @Autowired
    private AccountDao accountDao;


    /**
     * Business method
     *
     * The annotation Transactional() indicates transaction control for this class, and the transaction attribute is set in parentheses
     *                    When the annotation is on a class: set the transaction properties of all methods in the class
     *                   When the annotation is on a method: set the transaction properties of all methods of the method
     * @param outMan transfer accounts
     * @param inMan  Collection
     * @param money amount of money
     */
    @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED)
    @Override
    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan,money);
        accountDao.in(inMan,money);
    }
}

test case

package com.raphuscucullatus.controller;

import com.raphuscucullatus.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;

/**
 * @author raphus cucullatus
 * @version 2021/9/25 23:30
 * @since JDK8
 */
public class AccountController {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = classPathXmlApplicationContext.getBean(AccountService.class);
        accountService.transfer("tom","jerry",1000);
    }


}

3.2 annotation configuration declarative transaction control resolution

① Use @ Transactional to modify classes or methods that need transaction control. The attributes available for annotation are the same as xml configuration methods, such as isolation level, propagation behavior, etc.
② If annotations are used on a class, all methods under the class are configured with the same set of annotation parameters.
③ In terms of methods, different methods can adopt different transaction parameter configurations.
④ Annotation driven to enable transaction in Xml configuration file < TX: annotation driven / >

Key points of knowledge
Configuration points of annotated declarative transaction control
Platform transaction manager configuration (xml mode)
Configuration of transaction notification (@ Transactional annotation configuration)
Transaction annotation driven configuration TX: annotation driven/

Posted by awared on Sat, 25 Sep 2021 14:15:51 -0700