spring declarative transactions

spring declarative transactions

What is a transaction?

If a group of operations forms a business, the group of operations will either succeed or fail. An operation to ensure the integrity of business operations.

Example: for example, transfer, deduction from Zhang San's account and addition from Li Si's account must be successful at the same time.

Spring JdbcTemplate

In spring, in order to operate JDBC more conveniently, an abstraction layer is defined on the basis of JDBC. The purpose of this design is to provide template methods for different types of JDBC operations. Each template method can control the whole process and allow to cover specific tasks in the process. In this way, flexibility can be retained as much as possible and the workload of database access can be minimized.

Introduce dependency

        <!--spring Integrate third parties ORM This dependency will also be introduced at the same time spring-jdbc and spring-tx(affair)My bag and springIoc Foundation of jar package-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>

<!--        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.6.RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.6.RELEASE</version>
            <scope>compile</scope>
        </dependency>-->

Correspondence between driver and database version: https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-versions.html

Configure connection pool and JdbcTemplate objects

    <!--Configure scan-->
    <context:component-scan base-package="com.blog"/>
    <!--Import external profile-->
    <context:property-placeholder location="db.properties"/>
    <!--Configure connection pool objects-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${mysql.username}"/>
        <property name="password" value="${mysql.password}"/>
        <property name="url" value="${mysql.url}"/>
        <property name="driverClassName" value="${mysql.driverClassName}"/>
    </bean>
    <!--to configure jdbc-->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--named argument  jdbc Processing class-->
    <bean class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate" id="namedParameterJdbcTemplate">
        <constructor-arg type="javax.sql.DataSource" ref="dataSource"/>
    </bean>

Test method display

ClassPathXmlApplicationContext context;

@Before
public void before(){
    context = new ClassPathXmlApplicationContext("classpath:spring-ioc.xml");
}
@Test
public void test1(){
    DruidDataSource dataSource = context.getBean("dataSource", DruidDataSource.class);
    System.out.println(dataSource);
}
/*
* jdbcTemplate Connection test
* */
@Test
public void test2(){
    JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
    Integer integer = jdbcTemplate.queryForObject("select  count(1) from user", Integer.class);
    System.out.println(integer);
}

/*
 * jdbcTemplate Connection test
 * */
@Test
public void test3(){
    JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
    /*
    * If it is consistent with the database field
    * */
    //        User user = jdbcTemplate.queryForObject("select  count(1) from User", new BeanPropertyRowMapper<>(User.class));
    User o = jdbcTemplate.queryForObject("select  * from user where id=1",
            (resultSet, i) -> {
                /*Get data from result set*/
                User user = new User();
                user.setBalance(resultSet.getInt("BALANCE"));
                user.setId(resultSet.getInt("ID"));
                user.setCardno(resultSet.getString("CARD_NO"));
                return user;
            });
    System.out.println(o);
}

/*
 * Query entity list
 * */
@Test
public void test4(){
    JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);

    List<User> userList = jdbcTemplate.query("select  * from user", new RowMapper<User>() {
        @Override
        public User mapRow(ResultSet resultSet, int i) throws SQLException {
            /*Get data from result set*/
            User user = new User();
            user.setBalance(resultSet.getInt("BALANCE"));
            user.setId(resultSet.getInt("ID"));
            user.setCardno(resultSet.getString("CARD_NO"));
            return user;
        }
    });
    System.out.println(userList);
}

/*
 * newly added
 * */
@Test
public void test5(){
    JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
    int i = jdbcTemplate.update("insert into user (REAL_NAME, CARD_NO, BALANCE) VALUES (?,?,?)", "Li Si", "133", "546");
    System.out.println(i);
}
/*
 * modify
 * */
@Test
public void test6(){
    JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
    int i = jdbcTemplate.update("update user set BALANCE = ? where ID = 3",  "546");
    System.out.println(i);
}


/*
 * delete
 * */
@Test
public void test7(){
    JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
    int i = jdbcTemplate.update("delete from user where ID =?",  3);
    System.out.println(i);
}

/**
 * Named parameter processing NamedParameterJdbcTemplate
 */
@Test
public void test08(){
    NamedParameterJdbcTemplate jdbcTemplate = context.getBean(NamedParameterJdbcTemplate.class);

    Map<String,Object> map=new HashMap<>();
    map.put("id",2);

    // Modify similar
    User user = jdbcTemplate.queryForObject("select * from user where ID = :id", map, new RowMapper<User>() {
        @Override
        public User mapRow(ResultSet resultSet, int i) throws SQLException {

            /*Get data from result set*/
            User user = new User();
            // If the queried data is 0, null is returned
            if(i == 0){
                return null;
            }
            user.setBalance(resultSet.getInt("BALANCE"));
            user.setId(resultSet.getInt("ID"));
            user.setCardno(resultSet.getString("CARD_NO"));
            return user;
        }
    });

    System.out.println(user);
}

dao layer usage example

@Repository
public class UserDaoImpl implements UserDao {
    private JdbcTemplate jdbcTemplate;
    /*
    * JdbcTemplate Thread safe, which means that you can use the same instance of JdbcTemplate in multiple Daos. Of course, it can also be defined as a private attribute in the same dao
    * official: https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#jdbc-JdbcTemplate-idioms
    * This is the official recommended usage. One dao corresponds to one JdbcTemplate
    * */
    @Autowired
    public UserDaoImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    }

Four transaction features ACID

Four characteristics of ACID

  • A (Atomicity): Atomicity refers to either success or failure in a group of business operations, or submission or rollback in a group of add, delete, modify and query businesses

  • C (Consistency): the data before and after the transaction should be consistent. Change from one Consistency state to another. For example, Zhang San gave Li Si 1000 yuan, but the total amount of Zhang San and Li Si remained unchanged.

  • I (Isolation): in the case of multithreading, the execution of one transaction should not be affected by another transaction.

  • D (Durability): it is permanent when the data is changed after the transaction is committed.

Summary: in terms of transaction control, there are two main categories:

Programming transaction

Adding transaction processing logic directly into the code may require explicitly calling transaction initiation, commit, rollback, for example, beginTransaction(), commit(), rollback(), and other transaction management related methods in the code
connetion.autoCommit(false);
­­­­­­
­­­­
­­­
connction.commint()
catch(){
connction.rollback();
}

Declarative transaction

Add annotations outside the method or define it directly in the configuration file to separate the transaction management code from the business method and realize the transaction management in a declarative way. spring's AOP can exactly accomplish this function: the fixed mode of transaction management code, as a crosscutting concern, is modularized through AOP method, so as to realize declarative transaction

spring declarative transaction usage

Configure the transaction manager and enable annotation based transaction control mode

<!--Configure the transaction manager. Since the underlying operations of the transaction are connected to start the transaction, commit rollback and other operations, it is necessary to configure the data source-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="dataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--Enable annotation based transaction control mode tx Namespace
xmlns:tx="http://www.springframework.org/schema/tx
 If annotation configuration and xml All configuration, annotation first

@EnableTransactionManagement Add to configuration class
-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

Add the annotation @ Transactional on the required method

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Override
    public User getUser(){
        return userDao.getUser();
    }
    /*
    * It can be marked on the class (indicating that all methods are annotated) or on the method. If there is this annotation at the same time, the annotation on the method shall prevail
    * It is suggested to write in the method, and the strength is more detailed
    * It is recommended to download the business logic layer.
    * */
    @Override
    @Transactional
    public void trans() {
        userDao.sub();
        System.out.println("Zhang San deducted money to complete");
        int i = 1/0;
        userDao.add();

    }
}

@Transactional transaction attribute configuration

	isolation: Set the isolation level of transactions
    propagation: Propagation behavior of transactions
    noRollbackFor: Those abnormal transactions can not be rolled back
    noRollbackForClassName: The filled parameter is the full class name
    rollbackFor: Which abnormal transactions need to be rolled back
    rollbackForClassName: The filled parameter is the full class name
    readOnly: Sets whether a transaction is read-only  
    timeout: When the transaction exceeds the specified execution time, it will be automatically terminated and rolled back,The unit is seconds

Isolation sets the isolation level of transactions

Used to deal with some problems under concurrent transactions

    /*
    *Isolation Set the isolation level of transactions
    * Isolation.DEFAULT Use the default database isolation level -- default
    * Isolation.READ_UNCOMMITTED   Read uncommitted
    * Isolation.READ_COMMITTED    Read committed (non repeatable)
    * Isolation.REPEATABLE_READ Repeatable
    * Isolation.SERIALIZABLE Serial call
    * */
    @Override
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void trans() {
    	//Modify, delete and other operations
    }
Transaction isolation level
Transaction isolation levelDirty readingNon repeatable readingUnreal reading
Read uncommittedyesyesyes
Read committednoyesyes
Repeatable readnonoyes
serializablenonono

Dirty read: a transaction has two operations a and b. operation a modifies the database. At this time, another transaction reads the data modified by operation a, and then operation b fails to execute, and the transaction is rolled back.

Non repeatable reading: transaction a reads the database data, while transaction b modifies the database data during this process, resulting in inconsistent transaction results between the two reads of transaction a. Moreover, it can be modified after inconsistency.

Repeatable reading: after transaction a reads the data, transaction b modifies the data read by transaction a, so it is the same for transaction a to obtain the data again.

Serial call: a and b transactions are not executed at the same time, but are forcibly controlled to be executed successively

Query the default isolation level of the database

select @@tx_isolation

Program test - repeatable - unreal:

reference resources:

https://blog.csdn.net/sanyuesan0000/article/details/90235335?utm_term=mysql%E5%B9%BB%E8%AF%BB%E7%9A%84%E5%BD%B1%E5%93%8D&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allsobaiduweb~default-0-90235335&spm=3001.4430

https://blog.csdn.net/qq_31930499/article/details/110393988

Test of unreal reading under repeatable reading

Mvcc -- > multi version concurrency control

MVCC is explained in high performance MySQL as follows

InnoDB MVCC is realized by saving two hidden columns after each row of records. These two columns, one saves the creation time of the row and the other saves the expiration time (or deletion time) of the row. Of course, what is stored is not the actual time value, but the system version number. Each time a new transaction is started, the system version number will increase automatically. The system version number at the beginning of the transaction will be used as the transaction version number to compare with the version number of each row of records queried. Let's take a look at how MVCC operates at the REPEATABLE READ isolation level.

SELECT

InnoDB will check each row of records according to the following two conditions:

a. InnoDB only looks for data rows whose version is earlier than the current transaction Version (that is, the system version number of the row is less than or equal to the system version number of the transaction), which can ensure that the rows read by the transaction either exist before the start of the transaction, or have been inserted or modified by the transaction itself.

b. The deleted version of the row is either undefined or greater than the current transaction version number. This ensures that the rows read by the transaction are not deleted before the transaction starts. Only records that meet the above two conditions can be returned as query results.

INSERT

InnoDB saves the current system version number as the line version number for each newly inserted line.

DELETE

InnoDB saves the current system version number for each deleted line as the line deletion ID.

UPDATE

InnoDB inserts a new line of records, saves the current system version number as the line version number, and saves the current system version number to the original line as the line deletion ID.

Here is the test process

Transaction aTransaction b
T1select * from user
T2update user set balance=balance-200
T3update user set balance=balance-200insert into user (REAL_NAME, CARD_NO, BALANCE) VALUES (?,?,?) "," Li Si "," 133 "," 546 "
T4commit
T5select * from user
T6commit
T7

Understanding of test results: transaction a executes the update operation after query. Since the same data is updated as transaction b, and transaction b executes first, transaction a is waiting because of the lock problem, so transaction b can be modified only after it is submitted. At this time, the modified and newly added data of transaction b will be marked with the corresponding version number. At this time, transaction a modifies the data, and the submitted data of transaction b will be obtained due to the current read operation during modification, After modification, transaction b updates the version number of all data again, including the one just added by transaction b, so transaction a will find one more one again.

Propagation characteristics of propagation transactions

The propagation characteristic of transaction refers to that when a transaction method is called by another transaction method, the transaction method
How should it be carried out?
It is hoped that if there is an external transaction, the external transaction will be used, and if there is no external transaction, the transaction will be started by itself

The transaction opened on a is called the current transaction, and it is also called an external transaction relative to b and c

a(){
b();
c();
}
Transaction propagation behaviorExternal transaction does not existExternal transactionUsage scenario
REQUIRED (default)Start a new transactionIntegration into external transactions@Transactional(propagation = Propagation.REQUIRED) is applicable to addition, deletion, modification and query
SUPPORTSDo not open a new transactionIntegration into external transactions@Transactional(propagation = Propagation.SUPPORTS) applies to queries
REQUIRES_NEWStart a new transactionSuspend external transactions and create new transactions@Transactional (propagation = propagation. Requirements_new) is applicable to internal transactions and external transactions without business association, such as logs
NOT_SUPPORTEDDo not open a new transactionSuspend external transactions@Transactional (promotion =promotion.not\u supported) not commonly used
NEVERDo not open a new transactionThrow exception@Transactional(propagation = Propagation.NEVER) is not commonly used
MANDATORYThrow exceptionIntegration into external transactions@Transactional(propagation = Propagation.MANDATORY) is not commonly used

Timeout transaction execution timeout

Specifies the maximum time (in seconds) for a transaction to wait
When the current transaction accesses data, the data that may be accessed is locked by other data. At this time, the transaction must wait. If the waiting time is too long, it will cause poor experience for users.

@Transactional(timeout = 2)// In seconds

Set transaction read only (readOnly)

readonly: it will only be set in the business method of the query
connection.setReadOnly(true) notifies the database that the current database operation is read-only, and the database will optimize the current read-only operation accordingly
Usage scenario:
If you execute a single query statement at a time, it is not necessary to enable transaction support, and the database defaults
Support read consistency during SQL execution;
If you execute multiple query statements at one time, such as statistical query and report query, in this field
In this scenario, the overall read consistency of multiple SQL queries must be guaranteed. Otherwise, after the previous SQL query, the last SQL query
Before an SQL query, if the data is changed by other users, the overall statistical query will read data
In this case, transaction support should be enabled (such as setting non repeatability and phantom reading level)
Don't).

Exception attribute

Set the exceptions of the current transaction to rollback or commit.
By default, the rollback strategy is adopted for RuntimeException and its subclasses.
The default policy for Exception and its subclasses is submission.
1. Set which exceptions are not rolled back (noRollbackFor)
2. Set which exception rollbacks (rollbackFor)
@Transactional(timeout = 3,rollbackFor = {FileNotFoundException.class})

Use in practice

If the current business method is a group of add, modify and delete, you can set the transaction in this way
@Transactional
If the current business method is a group of queries, you can set transactions in this way
@Transactionl(readOnly=true)
If the current business method is a single query, you can set the transaction in this way
@Transactionl(propagation=propagation.SUPPORTS ,readOnly=true)

xml based usage

<?xml version="1.0" encoding="UTF-8"?>
<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 https://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 https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--Configure scan-->
    <context:component-scan base-package="com.blog"/>
    <!--Import external profile-->
    <context:property-placeholder location="db.properties"/>
    <!--Configure connection pool objects-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${mysql.username}"/>
        <property name="password" value="${mysql.password}"/>
        <property name="url" value="${mysql.url}"/>
        <property name="driverClassName" value="${mysql.driverClassName}"/>
    </bean>
    <!--to configure jdbc-->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--named argument  jdbc Processing class-->
    <bean class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate" id="namedParameterJdbcTemplate">
        <constructor-arg type="javax.sql.DataSource" ref="dataSource"/>
    </bean>
    <!--Configure the transaction manager. Since the underlying operations of the transaction are connected to start the transaction, commit rollback and other operations, it is necessary to configure the data source-->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="dataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--Enable annotation based transaction control mode tx Namespace
    xmlns:tx="http://www.springframework.org/schema/tx -->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

    <!--Declarative transaction passed aop Implementation: in different positions of the method, start, roll back and commit transactions through the connection object -->
    <aop:config>
        <!--Match all classes and methods in the business implementation layer-->
        <aop:pointcut id="transactionCut" expression="execution(* com.blog.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="myAdvice" pointcut-ref="transactionCut"/>
    </aop:config>

    <!--Specify the methods to which the pointcut matches and the transactions to declare-->
    <tx:advice id="myAdvice" transaction-manager="dataSourceTransactionManager">
        <tx:attributes>
            <!-- wildcard-->
            <tx:method name="update*"/>
            <tx:method name="delete*"/>
            <tx:method name="add*"/>
            <!--to configure get The method at the beginning is read-only, and the transaction will not be opened when the current transaction does not exist, and will be integrated into the current transaction when it exists-->
            <tx:method name="get" read-only="true" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>

</beans>

Tags: Spring Transaction

Posted by Zpixel on Thu, 14 Apr 2022 05:16:37 +0930