In-depth understanding of Spring transaction principles

 

 


First, the basic principle of
transaction The essence of Spring transaction is actually the support of the database for transactions. Without the transaction support of the database, spring cannot provide transaction functions. For pure JDBC operation database, if you want to use transaction, you can follow the following steps:
    get connection Connection con = DriverManager.getConnection()
    open transaction con.setAutoCommit(true/false);
    execute CRUD
    commit transaction/rollback transaction con.commit () / con.rollback();
    close the connection conn.close();
After using Spring's transaction management function, we can no longer write the code of steps 2 and 4, but Spirng will do it automatically. So how does Spring open and close transactions before and after the CRUD we write? To solve this problem, we can understand Spring's transaction management implementation principle as a whole. The following is a brief introduction. The annotation method
    enables the annotation driver for the example configuration file, and is marked by the annotation @Transactional on the relevant classes and methods.
    When spring starts, it will parse and generate related beans. At this time, it will check the classes and methods with related annotations, generate proxies for these classes and methods, and perform related configuration injection according to the relevant parameters of @Transaction, so that in the proxy In the middle, we have dealt with the related transactions (open the normal commit transaction, and abnormally roll back the transaction).
    The transaction commit and rollback of the real database layer is implemented through binlog or redo log.
Second, the propagation properties of Spring transactions
The so-called propagation property of spring transaction is to define how spring should handle the behavior of these transactions when multiple transactions exist at the same time. These properties are defined in TransactionDefinition, and the explanation of specific constants is shown in the following table:
Constant Name Constant Explanation
PROPAGATION_REQUIRED Supports the current transaction. If there is no current transaction, create a new transaction. This is the most common choice and is Spring's default transaction propagation.
PROPAGATION_REQUIRES_NEW Create a new transaction. If there is a current transaction, suspend the current transaction. The newly created transaction will have nothing to do with the suspended transaction. It is two independent transactions. After the outer transaction fails and rolls back, the execution result of the inner transaction cannot be rolled back. The inner transaction fails and an exception is thrown, and the outer transaction fails. Capture, or do not handle rollback operations
PROPAGATION_SUPPORTS supports the current transaction, if there is no current transaction, it is executed in a non-transactional manner.
PROPAGATION_MANDATORY supports the current transaction and throws an exception if there is no current transaction.
PROPAGATION_NOT_SUPPORTED Performs the operation in a non-transactional manner, suspending the current transaction if there is a current transaction.
PROPAGATION_NEVER executes non-transactionally and throws an exception if a transaction currently exists.
PROPAGATION_NESTED     
If an active transaction exists, run in a nested transaction. If there is no active transaction, it is executed according to the REQUIRED attribute. It uses a single transaction with multiple savepoints that can be rolled back. The rollback of the inner transaction does not affect the outer transaction. It only works on the DataSourceTransactionManager transaction manager.
3. Database isolation level
Isolation Level Value of Isolation Level Caused by Problems
Read-Uncommitted 0 Causes Dirty Reads
Read-Committed 1 Avoids Dirty Reads, Allows Non-repeatable Reads and Phantom Reads
Repeatable-Read 2 Avoids Dirty Reads, Non-repeatable Reads, Allows Phantom Reads
Serializable 3 Serializable With chemical read, transactions can only be executed one by one, avoiding dirty reads, non-repeatable reads, and phantom reads. The execution efficiency is slow, and the
dirty read is careful when using it: one transaction adds, deletes, or modifies the data, but it is not committed, and another transaction can read the uncommitted data. If the first transaction rolls back at this time, then the second transaction has read the dirty data.
Non-repeatable read: Two read operations occur in a transaction. Between the first read operation and the second operation, another transaction modifies the data. At this time, the two read data are inconsistent.
Phantom read: The first transaction modifies a certain range of data in batches, and the second transaction adds a piece of data to this range. At this time, the first transaction will lose the modification of the newly added data.
Summary:
The higher the isolation level, the better the integrity and consistency of data can be guaranteed, but the greater the impact on concurrent performance.
The default isolation level of most databases is Read Commited, such as SqlServer and Oracle. The
default isolation level of a few databases is: Repeatable Read For example: MySQL InnoDB
Fourth, the isolation level
constant in Spring Explanation
ISOLATION_DEFAULT This is the default isolation level of PlatfromTransactionManager, using the default isolation level of the database Transaction isolation level. The other four correspond to the isolation levels of JDBC.
ISOLATION_READ_UNCOMMITTED This is the minimum isolation level for a transaction, which allows another transaction to see the uncommitted data of this transaction. This isolation level produces dirty reads, non-repeatable reads and phantom reads.
ISOLATION_READ_COMMITTED ensures that data modified by one transaction can only be read by another transaction after it is committed. Another transaction cannot read the transaction's uncommitted data.
ISOLATION_REPEATABLE_READ This transaction isolation level can prevent dirty reads and non-repeatable reads. However, phantom reads may occur.
ISOLATION_SERIALIZABLE This is the most expensive but most reliable transaction isolation level. Transactions are processed for sequential execution.
Fifth, the nesting of transactions
Through the foreshadowing of the above theoretical knowledge, we roughly know some properties and characteristics of database transactions and spring transactions. Next, we will analyze some nested transaction scenarios to deeply understand the mechanism of spring transaction propagation.
Suppose Method A() of outer transaction Service A calls Method B() of inner Service B.
PROPAGATION_REQUIRED (spring default)
If the transaction level of ServiceB.methodB() is defined as PROPAGATION_REQUIRED, then spring has already executed ServiceA. A transaction is started, then ServiceB.methodB() is called. ServiceB.methodB() sees that it has already been running in the transaction of ServiceA.methodA(), and will not start a new transaction.
If ServiceB.methodB() finds itself not in a transaction when it runs, it assigns itself a transaction.
This way, if an exception occurs in ServiceA.methodA() or anywhere within ServiceB.methodB(), the transaction will be rolled back.
PROPAGATION_REQUIRES_NEW
For example, we design the transaction level of ServiceA.methodA() to be PROPAGATION_REQUIRED, and the transaction level of ServiceB.methodB() to be PROPAGATION_REQUIRES_NEW.
Then when ServiceB.methodB() is executed, the transaction where ServiceA.methodA() is located will be suspended, ServiceB.methodB() will start a new transaction, and it will wait for the transaction of ServiceB.methodB() to complete before it is completed. Go ahead.
The difference between him and the PROPAGATION_REQUIRED transaction is the degree of rollback of the transaction. Because ServiceB.methodB() is a new transaction, there are two different transactions. If ServiceB.methodB() has been committed, then ServiceA.methodA() fails to roll back, and ServiceB.methodB() will not roll back. If ServiceB.methodB() fails and rolls back, if the exception he throws is caught by ServiceA.methodA(), the ServiceA.methodA() transaction may still be committed (mainly depends on whether the exception thrown by B is an exception that A will roll back) .
PROPAGATION_SUPPORTS
Assuming that the transaction level of ServiceB.methodB() is PROPAGATION_SUPPORTS, then when ServiceB.methodB() is executed, if it is found that ServiceA.methodA() has already opened a transaction, it will join the current transaction. If it is found that ServiceA.methodA() does not If you open a transaction, you will not open the transaction yourself. At this point, the transactionality of the inner method is completely dependent on the outermost transaction.
The situation of PROPAGATION_NESTED
is now more complicated. The transaction property of ServiceB.methodB() is configured as PROPAGATION_NESTED. How will the two cooperate at this time? If ServiceB#methodB rollback, then the internal transaction (ie ServiceB#methodB ) will roll back to the SavePoint before it is executed and the external transaction (ie ServiceA#methodA) can have the following two processing methods:
a. Catch the exception and execute the exception branch logic
void methodA() { 
        try { 
            ServiceB.methodB(); 
        } catch (SomeException) { 
            // Execute other business, such as ServiceC.methodC(); 
        } 
    }
This method is also the most valuable part of nested transactions. It has the effect of branch execution. If ServiceB.methodB fails, then ServiceC.methodC() is executed, and ServiceB.methodB has been rolled back to the SavePoint before it was executed, so Dirty data will not be generated (equivalent to this method has never been executed), this feature can be used in some special business, and PROPAGATION_REQUIRED and PROPAGATION_REQUIRES_NEW have no way to do this.
b. The external transaction rollback/commit code does not make any changes, then if the internal transaction (ServiceB#methodB) rollback, then first ServiceB.methodB rolls back to the SavePoint before it was executed (in any case), the external transaction ( That is, ServiceA#methodA) will decide whether to commit or rollback according to the specific configuration. The
other three transaction propagation attributes are basically useless, and will not be analyzed here.
6. Summary
For the places where transactions need to be used in the project, I suggest that developers still use spring's TransactionCallback interface to implement transactions, and do not blindly use spring transaction annotations. If annotations must be used, then the propagation mechanism and There is a detailed understanding of the isolation level, otherwise unintended effects are likely to occur.


First, the basic principle of
transaction The essence of Spring transaction is actually the support of the database for transactions. Without the transaction support of the database, spring cannot provide transaction functions. For pure JDBC operation database, if you want to use transaction, you can follow the following steps:
    get connection Connection con = DriverManager.getConnection()
    open transaction con.setAutoCommit(true/false);
    execute CRUD
    commit transaction/rollback transaction con.commit () / con.rollback();
    close the connection conn.close();
After using Spring's transaction management function, we can no longer write the code of steps 2 and 4, but Spirng will do it automatically. So how does Spring open and close transactions before and after the CRUD we write? To solve this problem, we can understand Spring's transaction management implementation principle as a whole. The following is a brief introduction. The annotation method
    enables the annotation driver for the example configuration file, and is marked by the annotation @Transactional on the relevant classes and methods.
    When spring starts, it will parse and generate related beans. At this time, it will check the classes and methods with related annotations, generate proxies for these classes and methods, and perform related configuration injection according to the relevant parameters of @Transaction, so that in the proxy In the middle, we have dealt with the related transactions (open the normal commit transaction, and abnormally roll back the transaction).
    The transaction commit and rollback of the real database layer is implemented through binlog or redo log.
Second, the propagation properties of Spring transactions
The so-called propagation property of spring transaction is to define how spring should handle the behavior of these transactions when multiple transactions exist at the same time. These properties are defined in TransactionDefinition, and the explanation of specific constants is shown in the following table:
Constant Name Constant Explanation
PROPAGATION_REQUIRED Supports the current transaction. If there is no current transaction, create a new transaction. This is the most common choice and is Spring's default transaction propagation.
PROPAGATION_REQUIRES_NEW Create a new transaction. If there is a current transaction, suspend the current transaction. The newly created transaction will have nothing to do with the suspended transaction. It is two independent transactions. After the outer transaction fails and rolls back, the execution result of the inner transaction cannot be rolled back. The inner transaction fails and an exception is thrown, and the outer transaction fails. Capture, or do not handle rollback operations
PROPAGATION_SUPPORTS supports the current transaction, if there is no current transaction, it is executed in a non-transactional manner.
PROPAGATION_MANDATORY supports the current transaction and throws an exception if there is no current transaction.
PROPAGATION_NOT_SUPPORTED Performs the operation in a non-transactional manner, suspending the current transaction if there is a current transaction.
PROPAGATION_NEVER executes non-transactionally and throws an exception if a transaction currently exists.
PROPAGATION_NESTED     
If an active transaction exists, run in a nested transaction. If there is no active transaction, it is executed according to the REQUIRED attribute. It uses a single transaction with multiple savepoints that can be rolled back. The rollback of the inner transaction does not affect the outer transaction. It only works on the DataSourceTransactionManager transaction manager.
3. Database isolation level
Isolation Level Value of Isolation Level Caused by Problems
Read-Uncommitted 0 Causes Dirty Reads
Read-Committed 1 Avoids Dirty Reads, Allows Non-repeatable Reads and Phantom Reads
Repeatable-Read 2 Avoids Dirty Reads, Non-repeatable Reads, Allows Phantom Reads
Serializable 3 Serializable With chemical read, transactions can only be executed one by one, avoiding dirty reads, non-repeatable reads, and phantom reads. The execution efficiency is slow, and the
dirty read is careful when using it: one transaction adds, deletes, or modifies the data, but it is not committed, and another transaction can read the uncommitted data. If the first transaction rolls back at this time, then the second transaction has read the dirty data.
Non-repeatable read: Two read operations occur in a transaction. Between the first read operation and the second operation, another transaction modifies the data. At this time, the two read data are inconsistent.
Phantom read: The first transaction modifies a certain range of data in batches, and the second transaction adds a piece of data to this range. At this time, the first transaction will lose the modification of the newly added data.
Summary:
The higher the isolation level, the better the integrity and consistency of data can be guaranteed, but the greater the impact on concurrent performance.
The default isolation level of most databases is Read Commited, such as SqlServer and Oracle. The
default isolation level of a few databases is: Repeatable Read For example: MySQL InnoDB
Fourth, the isolation level
constant in Spring Explanation
ISOLATION_DEFAULT This is the default isolation level of PlatfromTransactionManager, using the default isolation level of the database Transaction isolation level. The other four correspond to the isolation levels of JDBC.
ISOLATION_READ_UNCOMMITTED This is the minimum isolation level for a transaction, which allows another transaction to see the uncommitted data of this transaction. This isolation level produces dirty reads, non-repeatable reads and phantom reads.
ISOLATION_READ_COMMITTED ensures that data modified by one transaction can only be read by another transaction after it is committed. Another transaction cannot read the transaction's uncommitted data.
ISOLATION_REPEATABLE_READ This transaction isolation level can prevent dirty reads and non-repeatable reads. However, phantom reads may occur.
ISOLATION_SERIALIZABLE This is the most expensive but most reliable transaction isolation level. Transactions are processed for sequential execution.
Fifth, the nesting of transactions
Through the foreshadowing of the above theoretical knowledge, we roughly know some properties and characteristics of database transactions and spring transactions. Next, we will analyze some nested transaction scenarios to deeply understand the mechanism of spring transaction propagation.
Suppose Method A() of outer transaction Service A calls Method B() of inner Service B.
PROPAGATION_REQUIRED (spring default)
If the transaction level of ServiceB.methodB() is defined as PROPAGATION_REQUIRED, then spring has already executed ServiceA. A transaction is started, then ServiceB.methodB() is called. ServiceB.methodB() sees that it has already been running in the transaction of ServiceA.methodA(), and will not start a new transaction.
If ServiceB.methodB() finds itself not in a transaction when it runs, it assigns itself a transaction.
This way, if an exception occurs in ServiceA.methodA() or anywhere within ServiceB.methodB(), the transaction will be rolled back.
PROPAGATION_REQUIRES_NEW
For example, we design the transaction level of ServiceA.methodA() to be PROPAGATION_REQUIRED, and the transaction level of ServiceB.methodB() to be PROPAGATION_REQUIRES_NEW.
Then when ServiceB.methodB() is executed, the transaction where ServiceA.methodA() is located will be suspended, ServiceB.methodB() will start a new transaction, and it will wait for the transaction of ServiceB.methodB() to complete before it is completed. Go ahead.
The difference between him and the PROPAGATION_REQUIRED transaction is the degree of rollback of the transaction. Because ServiceB.methodB() is a new transaction, there are two different transactions. If ServiceB.methodB() has been committed, then ServiceA.methodA() fails to roll back, and ServiceB.methodB() will not roll back. If ServiceB.methodB() fails and rolls back, if the exception he throws is caught by ServiceA.methodA(), the ServiceA.methodA() transaction may still be committed (mainly depends on whether the exception thrown by B is an exception that A will roll back) .
PROPAGATION_SUPPORTS
Assuming that the transaction level of ServiceB.methodB() is PROPAGATION_SUPPORTS, then when ServiceB.methodB() is executed, if it is found that ServiceA.methodA() has already opened a transaction, it will join the current transaction. If it is found that ServiceA.methodA() does not If you open a transaction, you will not open the transaction yourself. At this point, the transactionality of the inner method is completely dependent on the outermost transaction.
The situation of PROPAGATION_NESTED
is now more complicated. The transaction property of ServiceB.methodB() is configured as PROPAGATION_NESTED. How will the two cooperate at this time? If ServiceB#methodB rollback, then the internal transaction (ie ServiceB#methodB ) will roll back to the SavePoint before it is executed and the external transaction (ie ServiceA#methodA) can have the following two processing methods:
a. Catch the exception and execute the exception branch logic
void methodA() { 
        try { 
            ServiceB.methodB(); 
        } catch (SomeException) { 
            // Execute other business, such as ServiceC.methodC(); 
        } 
    }
This method is also the most valuable part of nested transactions. It has the effect of branch execution. If ServiceB.methodB fails, then ServiceC.methodC() is executed, and ServiceB.methodB has been rolled back to the SavePoint before it was executed, so Dirty data will not be generated (equivalent to this method has never been executed), this feature can be used in some special business, and PROPAGATION_REQUIRED and PROPAGATION_REQUIRES_NEW have no way to do this.
b. The external transaction rollback/commit code does not make any changes, then if the internal transaction (ServiceB#methodB) rollback, then first ServiceB.methodB rolls back to the SavePoint before it was executed (in any case), the external transaction ( That is, ServiceA#methodA) will decide whether to commit or rollback according to the specific configuration. The
other three transaction propagation attributes are basically useless, and will not be analyzed here.
6. Summary
For the places where transactions need to be used in the project, I suggest that developers still use spring's TransactionCallback interface to implement transactions, and do not blindly use spring transaction annotations. If annotations must be used, then the propagation mechanism and There is a detailed understanding of the isolation level, otherwise unintended effects are likely to occur.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324689607&siteId=291194637