From: www.jianshu.com/p/ce424cf4d…

Now that the previous chapters have examined spring’s @AspectJ-based source code, let’s take a look at another important feature of Aop, transaction management.

1. Database transaction features
  • Atomicity Multiple database operations are indivisible, and only when all operations are successfully executed can an item be committed; If any operation fails, all operations are rolled back and the database state must return to the state before the operation
  • Consistency After a successful transaction operation, the state of the database and the business rules must be consistent. For example, if you transfer 100 yuan from account A to account B, the total deposits of account A and account B remain the same no matter whether the database operation succeeds or fails.
  • Isolation When concurrent operations are performed, different database objects do not interfere with each other (although this level of isolation is also relevant)
  • After a persistent transaction is successfully committed, all data in the transaction must be persisted to the database. Even if the database crashes immediately after the transaction is committed, you need to ensure that the data can be recovered.
2. Transaction isolation level

Concurrent database operations may cause dirty reads, unrepeatable reads, unreal reads, type 1 update loss, and type 2 update loss.

  • Dirty reader A reads the change data that has not been committed by object B and modifies it. If object B rolls back, then the data read by object A is invalid, and A dirty read occurs.
  • Non-repeatable read A transaction executes the same query twice or more, getting different data each time. For example, when A queries the account balance, it happens that B transfers 100 yuan to the account, and A queries the account balance again, so the two query results of A are inconsistent.
  • Phantom read Object A reads the new data submitted by object B, at which point object A will appear phantom. Illusory and unrepeatable reading are easily confused. How do you distinguish them? A phantom read is a read of new data committed by another thing. A non-repeatable read is a read of changed data (modified or deleted) from an already committed thing.
  • The first type of loss updates A rollback that overwrites data already committed by B. For example, if the account has 1000 yuan, the withdrawal operation of 100 yuan from A is performed, but the thing is not submitted; At this point, business B deposits 100 yuan into the account and submits the account, and the account balance is changed to 1100 yuan. At this time, A rolled back the withdrawal operation, and the account balance was restored to 1000 yuan.
  • The second type of update loses the commit of thing A overwriting the data already committed by thing B. For example: the account has 1000 yuan, A business operation deposits 100 yuan to the account, but does not submit the thing; At this point, B withdrew 100 yuan from the account and submitted the account, and the account balance was changed to 900 yuan; At this point, A submits the transaction, and the account balance becomes 1100 yuan.

There are several solutions to the above problem, and setting database transaction isolation levels is one of them. Database transaction isolation levels are divided into four levels and are described in a table.

Isolation level Dirty read Unrepeatable read Read the illusion The first type is missing updates The second type of lost updates
READ UNCOMMITTED allow allow allow allow allow
READ COMMITTED Dirty read allow allow allow allow
REPEATABLE READ Don’t allow Don’t allow allow Don’t allow Don’t allow
SERIALIZABLE Don’t allow Don’t allow Don’t allow Don’t allow Don’t allow
3.Spring things support core interfaces

Spring transaction management core interface relationships

  • TransactionDefinition–> Interface to define spring-compatible transaction properties

Public interface TransactionDefinition {// If there is no current object, create a new object; If one already exists, add to it. int PROPAGATION_REQUIRED = 0; // Support the current thing, if there is no current thing, then non-thing. int PROPAGATION_SUPPORTS = 1; // Use the current thing, if there is no current thing, throw an exception. int PROPAGATION_MANDATORY = 2; // Create a new thing, and suspend the current thing if it already exists. int PROPAGATION_REQUIRES_NEW = 3; // Execute in non-transaction mode, suspends current transaction if current transaction exists. int PROPAGATION_NOT_SUPPORTED = 4; // Executes in a non-transaction manner, throwing an exception if something currently exists. int PROPAGATION_NEVER = 5; // Execute inside a nested object if the object currently exists; Int PROPAGATION_NESTED = 6 if there is no current thing, the same as PROPAGATION_REQUIRED; // Use the default isolation level for the back-end database. int ISOLATION_DEFAULT = -1; Int ISOLATION_READ_UNCOMMITTED = connection. TRANSACTION_READ_UNCOMMITTED; // 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 duration int TIMEOUT_DEFAULT = -1; Int getPropagationBehavior(); Int getIsolationLevel(); Int getTimeout(); Boolean isReadOnly(); @nullable String getName(); }Copy the code

  1. Spring transaction propagation feature table:
Propagation feature name instructions
PROPAGATION_REQUIRED If there is no current object, create a new object. If one already exists, add to it
PROPAGATION_SUPPORTS Support current things, and if there are no current things, execute in a non-thing way
PROPAGATION_MANDATORY Use the current thing, and throw an exception if there is no current thing
PROPAGATION_REQUIRES_NEW Create a new thing, and suspend the current thing if it already exists
PROPAGATION_NOT_SUPPORTED Executes in non-transaction mode, suspends the current thing if it currently exists
PROPAGATION_NEVER Executes in a non-transaction manner, throwing an exception if something currently exists
PROPAGATION_NESTED Execute within a nested thing if it currently exists; If nothing is currently present, it is the same as PROPAGATION_REQUIRED
  1. Spring Transaction Isolation Level table:
Isolation level Dirty read Unrepeatable read Read the illusion The first type is missing updates The second type of lost updates
ISOLATION_DEFAULT Same as backend database Same as backend database Same as backend database Same as backend database Same as backend database
ISOLATION_READ_UNCOMMITTED allow allow allow allow allow
ISOLATION_READ_COMMITTED Dirty read allow allow allow allow
ISOLATION_REPEATABLE_READ Don’t allow Don’t allow allow Don’t allow Don’t allow
ISOLATION_SERIALIZABLE Don’t allow Don’t allow Don’t allow Don’t allow Don’t allow
  • PlatformTransactionManager – > center interface in the Spring transaction infrastructure

Public interface PlatformTransactionManager {/ / according to the specified propagation behavior, returns the current active transaction or create a new transaction. TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException; // Commit the given transaction for the state of the given transaction. void commit(TransactionStatus status) throws TransactionException; // Perform a rollback for the given transaction. void rollback(TransactionStatus status) throws TransactionException; }Copy the code

Spring will manage things entrusted to the underlying persistence framework to complete, therefore, the Spring for different persistence framework provides different PlatformTransactionManager interface implementation. To name a few of Spring’s built-in transaction managers:

Transaction manager instructions
org.springframework.jdbc.datasource.DataSourceTransactionManager Provides transaction management for a single Javax.sqL. DataSource for the Spring JDBC abstraction framework, iBATIS, or MyBatis framework
org.springframework.orm.jpa.JpaTransactionManager Provide for a single javax.mail. Persistence. EntityManagerFactory transaction support, used to integrate JPA implementation framework of transaction management
org.springframework.transaction.jta.JtaTransactionManager Provides support for distributed transaction management and delegates transaction management to the Java EE application server transaction manager
  • TransactionStatus–> TransactionStatus description
  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 be running in an actual transaction to begin with.) Boolean isNewTransaction(); // Returns whether the transaction carries savepoints internally, that is, has been created as a nested transaction based on savepoints. boolean hasSavepoint(); // Set transaction rollback only. voidsetRollbackOnly(); Boolean isRollbackOnly(); // Returns whether the transaction is marked for rollback only. // Flush the session to the datastore @override void flush(); // Returns whether the thing has been completed, whether committed or rolled back. boolean isCompleted(); }Copy the code

  1. SavepointManager interface

Public interface SavepointManager {// Create a new savepoint. Object createSavepoint() throws TransactionException; // Roll back to the given savepoint. // Note that a savepoint is not automatically released after being rolled back to a given savepoint. The releaseSavepoint method can be released. void rollbackToSavepoint(Object savepoint) throws TransactionException; // Explicitly release the given savepoint. (Most transaction managers automatically release savepoints upon transaction completion.) Void releaseSavepoint(Object SavePoint) throws TransactionException; }Copy the code

  1. Flushable interface

Public interface Flushable {// Flushes sessions to the data store void Flush () throws IOException; }Copy the code

Spring programming things
  • table

CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Increment primary key',
  `balance` int(11) DEFAULT NULL COMMENT 'Account balance',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='-- Account table '
Copy the code

  • implementation

package com.lyc.cn.v2.day08; import org.apache.commons.dbcp.BasicDataSource; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import javax.sql.DataSource; /** * Spring programming things * @author: LiYanChao * @create: 2018-11-09 11:41 */ public class MyTransaction { private JdbcTemplate jdbcTemplate; private DataSourceTransactionManager txManager; private DefaultTransactionDefinition txDefinition; private String insert_sql ="insert into account (balance) values ('100')";

    public void save{// 1, initialize jdbcTemplate DataSource DataSource = DataSource(); jdbcTemplate = new JdbcTemplate(dataSource); / / 2, create content manager txManager = new DataSourceTransactionManager (); txManager.setDataSource(dataSource); / / 3, define things attribute txDefinition = new DefaultTransactionDefinition (); txDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus txStatus = txManager.getTransaction(txDefinition); Try {jdbctemplate. execute(insert_sql); //int i = 1/0; jdbcTemplate.execute(insert_sql); txManager.commit(txStatus); } catch (DataAccessException e) { txManager.rollback(txStatus); e.printStackTrace(); } } public DataSourcegetDataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/my_test? useSSL=false&useUnicode=true&characterEncoding=UTF-8");
        dataSource.setUsername("root");
        dataSource.setPassword("liyanchao1989@");
        returndataSource; }}Copy the code

  • Add Gradle modules and packages

// Add spring-JDBC module optional(project(":spring-jdbc"))
// https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp
compile group: 'commons-dbcp', name: 'commons-dbcp', version: '1.4'
// https://mvnrepository.com/artifact/mysql/mysql-connector-java
compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.38'
Copy the code

  • Test classes and results

package com.lyc.cn.v2.day08;

import org.junit.Test;

/**
 * @author: LiYanChao
 * @create: 2018-11-07 18:45
 */
public class MyTest {
    @Test
    public void test1() { MyTransaction myTransaction = new MyTransaction(); myTransaction.save(); }}Copy the code

Run the test class and roll back things manually after throwing an exception, so no records are added to the database tables.