Spring transaction mechanism mainly includes declarative transaction and programmatic transaction, here focuses on the explanation of declarative transaction, programmatic transaction is not widely used in the actual development, only for learning reference.
Spring declarative transactions free us from complex transaction processing. This eliminates the need to deal with getting a connection, closing a connection, committing a transaction, and rolling back. No longer do we need to handle a lot of tries in transaction-dependent methods… The catch… Finally the code. A very important concept when we use Spring declarative transactions is transaction properties. Transaction attributes typically consist of the propagation behavior of the transaction, the isolation level of the transaction, the timeout value of the transaction, and the read-only flag of the transaction. When we do transaction partitioning, we need to do transaction definition, that is, to configure the properties of the transaction.
The following is a detailed explanation of the four properties of transactions for your reference only:
Spring in TransactionDefinition interface defined in these properties, used for PlatfromTransactionManager, PlatfromTransactionManager is the core of the Spring affairs management interface.
public interface TransactionDefinition {
int getPropagationBehavior(a); // Returns the propagation behavior of the transaction.
int getIsolationLevel(a); // Returns the isolation level of a transaction by which the transaction manager controls what data another transaction can see within the transaction.
int getTimeout(a); // Returns the number of seconds in which the transaction must complete.
boolean isReadOnly(a); // If the transaction is read-only, the transaction manager can optimize based on this return value to ensure that the transaction is read-only.
}
Copy the code
- Five isolation levels are defined in the TransactionDefinition interface:
ISOLATION_DEFAULT a PlatfromTransactionManager the default isolation level, using the database to the default transaction isolation level. The other four correspond to JDBC isolation levels;
ISOLATION_READ_UNCOMMITTED This is the lowest isolation level for a transaction and 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 is committed before it can be read by another transaction. Another transaction cannot read uncommitted data from that transaction. This transaction isolation level avoids dirty reads, but non-repeatable and phantom reads may occur.
The ISOLATION_REPEATABLE_READ transaction isolation level prevents dirty, non-repeatable reads. But illusionary reading can occur. In addition to ensuring that one transaction cannot read uncommitted data from another transaction, it also ensures that the following situation (unrepeatable reads) is avoided.
ISOLATION_SERIALIZABLE This is the most expensive but reliable transaction isolation level. Transactions are processed for sequential execution. In addition to preventing dirty read, not repeat read, but also avoid phantom read.
- Seven transaction propagation behaviors are defined in the TransactionDefinition interface:
(1) PROPAGATION_REQUIRED The current transaction is supported if a transaction exists. If there are no transactions, start a new one.
// The transaction attribute PROPAGATION_REQUIREDMethodA {... methodB(); ... }// The transaction attribute PROPAGATION_REQUIREDMethodB {... }Copy the code
Using Spring declarative transactions, Spring uses AOP to support declarative transactions. Based on transaction attributes, it automatically decides whether to start a transaction before a method is called and whether to commit or roll back a transaction after the method is executed.
Call methodB method alone:
main { metodB(); } equivalent to Main {Connection con=null;
try{
con = getConnection();
con.setAutoCommit(false);
// method call
methodB();
// Commit the transaction
con.commit();
} Catch(RuntimeException ex) {
// Rollback the transaction
con.rollback();
} finally {
// Release resourcescloseCon(); }}Copy the code
Spring guarantees that all calls to the methodB method get the same connection. When methodB is called, no transaction exists, so a new connection is obtained and a new transaction is opened.
When MethodA is called separately, MethodB is called within MethodA. The implementation effect is equivalent to:
Main {
Connection con = null;
try {
con = getConnection();
methodA();
con.commit();
} catch(RuntimeException ex) {
con.rollback();
} finally{ closeCon(); }}Copy the code
When MethodA is called, there are no transactions in the environment, so start a new transaction. When MethodB is called in MethodA, there is already a transaction in the environment, so MethodB joins the current transaction.
(2) PROPAGATION_SUPPORTS the current transaction if a transaction exists. If there is no transaction, non-transactional execution. However, for transactionally-synchronized transaction managers, PROPAGATION_SUPPORTS is slightly different from not using transactions.
// The transaction attribute PROPAGATION_REQUIRED
methodA() {
methodB();
}
// Transaction attribute PROPAGATION_SUPPORTSMethodB () {... }Copy the code
When methodB is simply called, the methodB method is non-transactional. When methdA is called,methodB joins methodA’s transaction.
(3) PROPAGATION_MANDATORY, the current transaction is supported if a transaction already exists. If there is no active transaction, an exception is thrown.
// The transaction attribute PROPAGATION_REQUIRED
methodA() {
methodB();
}
// Transaction attribute PROPAGATION_MANDATORYMethodB () {... }Copy the code
When methodB is called separately, because there is currently no transaction active, An exception will throw new IllegalTransactionStateException (” Transaction propagation ‘mandatory’ but no existing Transaction found”); When methodA is called, methodB is added to the methodA transaction.
(4) Always open a new transaction, PROPAGATION_REQUIRES_NEW If a transaction already exists, suspend the existing transaction.
// The transaction attribute PROPAGATION_REQUIRED
methodA() {
doSomeThingA();
methodB();
doSomeThingB();
}
// The transaction attribute PROPAGATION_REQUIRES_NEWMethodB () {... }Copy the code
main() {
methodA();
}
Copy the code
Is equivalent to:
main() {
TransactionManager tm = null;
try {
// Get a JTA transaction manager
tm = getTransactionManager();
tm.begin();
// Start a new transaction
Transaction ts1 = tm.getTransaction();
doSomeThing();
tm.suspend(); // Suspend the current transaction
try {
tm.begin();// Restart the second transaction
Transaction ts2 = tm.getTransaction();
methodB();
ts2.commit();// Commit the second transaction
} catch(RunTimeException ex) {
ts2.rollback(); // Rollback the second transaction
} finally {
// Release resources
}
MethodB = methodB; // methodB = methodB
tm.resume(ts1);
doSomeThingB();
ts1.commit();// Commit the first transaction
} catch(RunTimeException ex) {
ts1.rollback();// Rollback the first transaction
} finally {
// Release resources}}Copy the code
Here, I refer to TS1 as the outer transaction and TS2 as the inner transaction. As you can see from the above code, TS2 and TS1 are two separate transactions, unrelated to each other. The success of Ts2 does not depend on TS1. If methodA fails a doSomeThingB method after calling methodB, the methodB method is still committed. The results of any code other than methodB are rolled back. To use PROPAGATION_REQUIRES_NEW, you need to use JtaTransactionManager as the transaction manager.
(5) Always execute non-transactionally, PROPAGATION_NOT_SUPPORTED, and suspend any transactions that exist. To use PROPAGATION_NOT_SUPPORTED, you also need to use JtaTransactionManager as the transaction manager.
(6) Always execute non-transactionally, PROPAGATION_NEVER, and throw an exception if an active transaction exists;
(7) Execute a nested transaction, PROPAGATION_NESTED, if an active transaction exists. If there is no active transaction, then the TransactionDefinition. PROPAGATION_REQUIRED properties is carried out. This is a nested transactions, using JDBC 3.0 drivers, support only DataSourceTransactionManager as a transaction manager. A java.sql.Savepoint class with a JDBC driver is required. Some JTA transaction manager implementations may provide the same functionality. Use PROPAGATION_NESTED, still need to turn the PlatformTransactionManager nestedTransactionAllowed attribute set to true; The default value of the nestedTransactionAllowed attribute is false.
// The transaction attribute PROPAGATION_REQUIRED
methodA() {
doSomeThingA();
methodB();
doSomeThingB();
}
// Transaction attribute PROPAGATION_NESTEDMethodB () {... }Copy the code
If the methodB method is called separately, the REQUIRED attribute is executed. If the methodA method is called, it does the following:
main() {
Connection con = null;
Savepoint savepoint = null;
try{
con = getConnection();
con.setAutoCommit(false);
doSomeThingA();
savepoint = con2.setSavepoint();
try{
methodB();
} catch(RuntimeException ex) {
con.rollback(savepoint);
} finally {
// Release resources
}
doSomeThingB();
con.commit();
} catch(RuntimeException ex) {
con.rollback();
} finally {
// Release resources}}Copy the code
Before methodB is called, setSavepoint is called to save the current state to SavePoint. If methodB fails, the methodB method is restored to the previously saved state. Note, however, that the transaction is not committed, and all operations including methodB are rolled back if the subsequent code (doSomeThingB() method call) fails.
A very important concept of nested transactions is that the inner transactions depend on the outer transactions. When an outer transaction fails, actions taken by the inner transaction are rolled back. The failure of the inner transaction does not cause the rollback of the outer transaction.
PROPAGATION_REQUIRES_NEW
They are very similar in that they are like a nested transaction, and if there is no active transaction, a new one will be started. With PROPAGATION_REQUIRES_NEW, an inner and outer transaction are like two separate transactions. Once an inner transaction has committed, it cannot be rolled back by an outer transaction. The two matters do not affect each other. Two transactions are not a true nested transaction. It also requires the support of the JTA transaction manager.
A rollback of an outer transaction can cause a rollback of an inner transaction, PROPAGATION_NESTED. The exception of the inner transaction does not cause the rollback of the outer transaction, which is a true nested transaction. DataSourceTransactionManager use savepoint PROPAGATION_NESTED support requires more than JDBC 3.0 driver and JDK version 1.4 +. Other JTA TrasactionManager implementations may be supported in different ways.
PROPAGATION_REQUIRES_NEW Starts a new, environment-independent “internal” transaction. The transaction will be fully commited or rolled back without dependence on external transactions, it will have its own isolation scope, its own locks, etc. The external transaction is suspended when the internal transaction begins execution, and resumes when the internal transaction ends.
On the other hand, PROPAGATION_NESTED starts a “nested” transaction, which is a true subtransaction of an existing transaction. When the nested transaction starts executing, it takes a SavePoint. If the nested transaction fails, we roll back to the savePoint. A nested transaction is part of an external transaction and is committed only after the external transaction is completed.
The biggest difference between PROPAGATION_REQUIRES_NEW and PROPAGATION_NESTED is that PROPAGATION_REQUIRES_NEW is completely a new transaction, If an external transaction is committed, a nested transaction is committed, PROPAGATION_NESTED is a subtransaction of an external transaction. This rule also applies to roll back. PROPAGATION_REQUIRED should be our first transaction propagation behavior. It satisfies most of our transaction needs.