Spring 5 Source Deep Resolution - - @Transactional Annotation Declarative Introduction (100% Understanding Transactions)

Several chapters have analyzed spring's @AspectJ-based source code, so let's look at another important function of Aop, Things Management.

Introduction to the transaction

1. Characteristics of Database Things

  • Atomicity
    Multiple database operations are inseparable, and only when all operations are successful, can things be submitted; as long as one operation fails, all operations must be rolled back, and the state of the database must be restored to the state before the operation.
  • Uniformity
    When things are successfully operated, the state of the database must be consistent with the business rules. For example, transferring 100 yuan from A account to B account, regardless of the success or failure of database operation, the total amount of deposits in A and B accounts remains unchanged.
  • Isolation
    When operating concurrently, different database objects do not interfere with each other (of course, this level of isolation is also relevant).
  • Persistence
    After a successful submission, all data in a transaction must be persisted to the database. Even if the database crashes immediately after a submission, it needs to be guaranteed that the data can be recovered.

2. Isolation Level

When the database is operated concurrently, it may cause dirty reading, non-repeatable reading, hallucination, loss of updates in the first category and loss of updates in the second category.

  • Dirty reading
    Thing A reads and modifies the change data that Thing B has not submitted; if Thing B rolls back, then the data read by Thing A is invalid, and dirty reading occurs.
  • Non-repeatable reading
    A transaction performs the same query twice or more, and each time it receives different data. For example: A thing under the account balance query, at this time coincidentally B thing to account transfer 100 yuan, A thing again query account balance, then A thing two query results are inconsistent.
  • Phantom reading
    Things A read the new data submitted by things B, at which time things A will appear hallucination phenomenon. Illusional reading and non-repeatable reading are easy to confuse. How to distinguish them? Hallucination is to read the new data submitted by other things, and non-repeatable reading is to read the changed data (modification or deletion) of the submitted things.

For the above problems, there are many solutions. One of them is to set the isolation level of database transactions. The isolation level of database transactions is divided into four levels, and its function is described by a table.

Isolation level Dirty reading Non-repeatable reading Phantom reading
READ UNCOMMITTED allow allow allow
READ COMMITTED Dirty reading allow allow
REPEATABLE READ Not allow Not allow allow
SERIALIZABLE Not allow Not allow Not allow

3.Spring Things Support Core Interfaces

 
  • Transaction Definition - > Defines an interface for spring-compatible transaction attributes
public interface TransactionDefinition {
    // If there is nothing at present, create a new thing; if there is already one thing, add it to it.
    int PROPAGATION_REQUIRED = 0;
    // Supports the current things, if there is nothing at present, then executes in a non-tangible way.
    int PROPAGATION_SUPPORTS = 1;
    // Using the current thing, throw an exception if there is no current thing.
    int PROPAGATION_MANDATORY = 2;
    // New things, if existing things, then hang up the current things.
    int PROPAGATION_REQUIRES_NEW = 3;
    // Execute in a non-tangible way, and suspend the current thing if it exists.
    int PROPAGATION_NOT_SUPPORTED = 4;
    // Execute in a non-tangible manner and throw an exception if something is present.
    int PROPAGATION_NEVER = 5;
    // If there are currently things, they are executed within nested things; if there are no currently things, they are executed within nested things. PROPAGATION_REQUIRED The same propagation characteristics
    int PROPAGATION_NESTED = 6;
    // Use the default isolation level of the back-end database.
    int ISOLATION_DEFAULT = -1;
    // READ_UNCOMMITTED Isolation level
    int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
    // READ_COMMITTED Isolation level
    int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
    // REPEATABLE_READ Isolation level
    int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
    // SERIALIZABLE Isolation level
    int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
    // Default timeout
    int TIMEOUT_DEFAULT = -1;
    // Obtaining the transmission characteristics of things
    int getPropagationBehavior();
    // Get the isolation level of things
    int getIsolationLevel();
    // Getting Things Overtime
    int getTimeout();
    // Judging whether something is readable
    boolean isReadOnly();
    // Get the name of the thing
    @Nullable
    String getName();
}
  1. Spring Thing Communication Characteristic Table:
Name of propagation feature Explain
PROPAGATION_REQUIRED If there is nothing at present, create a new thing; if there is already something, add it to it.
PROPAGATION_SUPPORTS Supporting the current thing, if there is nothing at present, then execute in a non-tangible way
PROPAGATION_MANDATORY Using the current thing, throw an exception if there is no current thing
PROPAGATION_REQUIRES_NEW New things, hang things if they already exist
PROPAGATION_NOT_SUPPORTED Execute in a non-tangible way, suspend the current thing if it exists
PROPAGATION_NEVER Execute in a non-tangible way, throw an exception if something is present
PROPAGATION_NESTED

If there are currently things, they are executed within nested things.

If there is nothing at present, it has the same propagation characteristics as PROPAGATION_REQUIRED.

  1. Spring Things Isolation Level Table:
Transaction isolation level Dirty reading Non-repeatable reading Phantom reading
read-uncommitted Yes. Yes. Yes.
read-committed No Yes. Yes.
repeatable-read No No Yes.
serializable No No No

mysql's default transaction isolation level is repeatable-read

3. Platform Transaction Manager - > Central Interface in Spring Transaction Infrastructure

public interface PlatformTransactionManager {
    // Returns the current active transaction or creates a new transaction based on the specified propagation behavior.
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
    // Submit a given transaction for the state of a given transaction.
    void commit(TransactionStatus status) throws TransactionException;
    // Rollback to perform a given transaction.
    void rollback(TransactionStatus status) throws TransactionException;
}

Spring delegates the management of things to the underlying persistence framework, so Spring provides different Platform Transaction Manager interface implementations for different persistence frameworks. List some of Spring's own Things Managers:

Things Manager Explain
org.springframework.jdbc.datasource.DataSourceTransactionManager Provides transaction management for a single javax.sql.DataSource for transaction management in Spring JDBC Abstract framework, iBATIS or MyBatis framework
org.springframework.orm.jpa.JpaTransactionManager Provides transaction support for a single javax.persistence.EntityManagerFactory for transaction management when integrating the JPA implementation framework
org.springframework.transaction.jta.JtaTransactionManager Provide support for distributed transaction management and delegate transaction management to Java EE application server transaction manager

 

  • Transaction Status - > Description of the State of Things
  1. TransactionStatus interface
public interface TransactionStatus extends SavepointManager, Flushable {
    // Returns whether the current transaction is a new transaction (otherwise it will participate in an existing transaction, or it may not run in a real transaction at first)
    boolean isNewTransaction();
    // Returns whether the transaction carries a savepoint internally, that is, it has been created as a nested savepoint-based transaction.
    boolean hasSavepoint();
    // Set the transaction to roll back only.
    void setRollbackOnly();
    // Whether the return transaction has been marked as rollback only
    boolean isRollbackOnly();
    // Refresh session to data store
    @Override
    void flush();
    // Returns whether the object has been completed, whether submitted or rolled back.
    boolean isCompleted();
}
  1. SavepointManager interface
public interface SavepointManager {
    // Create a new savepoint.
    Object createSavepoint() throws TransactionException;
    // Roll back to the given savepoint.
    // Note: Calling this method to roll back to a given savepoint will not automatically release the savepoint.
    // By calling releaseSavepoint Method Release the preservation point.
    void rollbackToSavepoint(Object savepoint) throws TransactionException;
    // Explicit release of a given savepoint. (Most transaction managers automatically release savepoints when transactions are completed)
    void releaseSavepoint(Object savepoint) throws TransactionException;
}

Spring Programming Things

  • surface
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Self increasing primary key',
  `balance` int(11) DEFAULT NULL COMMENT 'Account balance',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='--Account statement'
  • Realization
 1 import org.apache.commons.dbcp.BasicDataSource;
 2 import org.springframework.dao.DataAccessException;
 3 import org.springframework.jdbc.core.JdbcTemplate;
 4 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 5 import org.springframework.transaction.TransactionDefinition;
 6 import org.springframework.transaction.TransactionStatus;
 7 import org.springframework.transaction.support.DefaultTransactionDefinition;
 8 
 9 import javax.sql.DataSource;
10 
11 /**
12  * Spring Programming Things
13  * @author: Chenhao
14  * @create: 2019-10-08 11:41
15  */
16 public class MyTransaction {
17 
18     private JdbcTemplate jdbcTemplate;
19     private DataSourceTransactionManager txManager;
20     private DefaultTransactionDefinition txDefinition;
21     private String insert_sql = "insert into account (balance) values ('100')";
22 
23     public void save() {
24 
25         // 1,Initialization jdbcTemplate
26         DataSource dataSource = getDataSource();
27         jdbcTemplate = new JdbcTemplate(dataSource);
28 
29         // 2,Creation Manager
30         txManager = new DataSourceTransactionManager();
31         txManager.setDataSource(dataSource);
32 
33         // 3,Define the attributes of things
34         txDefinition = new DefaultTransactionDefinition();
35         txDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
36 
37         // 3,Opening things
38         TransactionStatus txStatus = txManager.getTransaction(txDefinition);
39 
40         // 4,Executing business logic
41         try {
42             jdbcTemplate.execute(insert_sql);
43             //int i = 1/0;
44             jdbcTemplate.execute(insert_sql);
45             txManager.commit(txStatus);
46         } catch (DataAccessException e) {
47             txManager.rollback(txStatus);
48             e.printStackTrace();
49         }
50 
51     }
52 
53     public DataSource getDataSource() {
54         BasicDataSource dataSource = new BasicDataSource();
55         dataSource.setDriverClassName("com.mysql.jdbc.Driver");
56         dataSource.setUrl("jdbc:mysql://localhost:3306/my_test?useSSL=false&useUnicode=true&characterEncoding=UTF-8");
57         dataSource.setUsername("root");
58         dataSource.setPassword("chenhao1991@");
59         return dataSource;
60     }
61 
62 }
  • Test classes and results
public class MyTest {
    @Test
    public void test1() {
        MyTransaction myTransaction = new MyTransaction();
        myTransaction.save();
    }
}

Running the test class manually rolls things back after throwing an exception, so no records will be added to the database table.

Declarative Things Based on @Transactional Annotations

The bottom layer is based on AOP, intercepts the method before and after, creates or joins a transaction before the target method starts, and submits or rolls back the transaction according to the implementation after the target method is executed. By declarative things, transaction rules can be applied to business logic by declaring relevant transaction rules in configuration files (or by equivalent annotation-based way) without mixing transaction management code with business logic code.

  • Interface
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * Account interface
 * @author: ChenHao
 * @create: 2019-10-08 18:38
 */
@Transactional(propagation = Propagation.REQUIRED)
public interface AccountServiceImp {
    void save() throws RuntimeException;
}
  • Realization
import org.springframework.jdbc.core.JdbcTemplate;

/**
 * Account Interface Implementation
 * @author: ChenHao
 * @create: 2019-10-08 18:39
 */
public class AccountServiceImpl implements AccountServiceImp {

    private JdbcTemplate jdbcTemplate;

    private static String insert_sql = "insert into account(balance) values (100)";


    @Override
    public void save() throws RuntimeException {
        System.out.println("==Start execution sql");
        jdbcTemplate.update(insert_sql);
        System.out.println("==End of execution sql");

        System.out.println("==Prepare to throw an exception");
        throw new RuntimeException("==Manually throw an exception");
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
}
  • configuration file
<?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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--open tx annotation-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!--Things Manager-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--data source-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/my_test?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="chenhao1991@"/>
    </bean>

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

    <!--business bean-->
    <bean id="accountService" class="com.chenhao.aop.AccountServiceImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

</beans>
  • test
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author: ChenHao
 * @create: 2019-10-08 18:45
 */
public class MyTest {

    @Test
    public void test1() {
        // Be based on tx Declarative things of labels
        ApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml");
        AccountServiceImp studentService = ctx.getBean("accountService", AccountServiceImp.class);
        studentService.save();
    }
}
  • test
==Start execution sql
==End of execution sql
==Prepare to throw an exception

java.lang.RuntimeException: ==Manually throw an exception

    at com.lyc.cn.v2.day09.AccountServiceImpl.save(AccountServiceImpl.java:24)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)

An exception is thrown manually in the test method, and Spring rolls things back automatically. Looking at the database, you can see that there are no new records.

Note: By default, transaction processing in Spring only rolls back the RuntimeException method, so replacing RuntimeException with ordinary Exception here will not have a rollback effect.

Next, we analyze the source implementation of declarative things based on @Transactional annotations.

 
 

Keywords: Java Spring Database JDBC

Added by Noumes on Sat, 12 Oct 2019 06:43:59 +0300