When we are developing with the Spring Framework, we often use theservice
layer writes a lot of methods, and they are all methods with the(political, economic etc) affairs
s, so how do Spring transactions propagate across multiple methods? Let's have a closer chat today.
Spring's transaction propagation mechanism mainly solves the problem of how transactions are passed between multiple methods, and there are usually seven types of propagation:
- REQUIRED
- SUPPORTS
- MANDATORY
- REQUIRES_NEW
- NOT_SUPPORTED
- NEVER
- NESTED
Below we demonstrate how each of these 7 types work.
base code
Before we explain the 7 types of propagation, let's take a look at the base code, which is very simple, so let's familiarize ourselves with it:
public void outerTransaction() {
//Inserting text into a table“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("outerTransaction");
(tp);
//invocationsinnerTransactionmethodologies
TransactionPropagationService currentProxy = (TransactionPropagationService)();
();
//throw an exception
int i = 1 / 0 ;
}
public void innerTransaction() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction");
(tp);
//throw an exception
int i = 1 / 0 ;
}
methodologiesouterTransaction()
Insert the text "outerTransaction" into the table and call theinnerTransaction()
method, and finally by calculating the1 / 0
Throw an exception.
methodologiesinnerTransaction()
Insert the text "innerTransaction" into the table by calculating the1 / 0
Throw an exception.
Here we are calling theinnerTransaction()
method, first get the current AOP proxy, and then call it through the proxy. This is because the two methods are in the same class, and if they are called directly without going through the proxy, they will be out of Spring transaction AOP management, resulting in a transaction failure.
We use annotations on these two methods and configure different propagation mechanisms to demonstrate the effect of different propagation mechanisms by seeing if the database inserts data successfully.
REQUIRED
REQUIRED
is Spring's default propagation mechanism, __ Meaning:__ If a transaction currently exists, join the transaction, if no transaction exists, create a transaction. Below we demonstrate respectively:
- If no transaction exists, create one. The specific code is as follows:
public void outerTransaction() {
//Inserting text into a table“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("outerTransaction");
(tp);
//call (programming)innerTransactionmethodologies
TransactionPropagationService currentProxy = (TransactionPropagationService)();
();
}
@Transactional(propagation = )
public void innerTransaction() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction");
(tp);
//throw an exception
int i = 1 / 0 ;
}
The outerTransaction() method does not have a transaction annotation, although an exception is thrown when the innerTransaction() method is called and the insertion of the data should be successful. innerTransaction() method has a transaction annotation that is propagated:REQUIRED
, since there is no transaction in outerTransaction(), it will create a new transaction with an exception thrown after it, so the data will not be inserted successfully, let's test it and see how it turns out?
was in line with our expectations.innerTransaction()
A new transaction was created and the data was not inserted successfully because an exception was thrown.
- If a transaction currently exists, join it with the following code:
@Transactional(propagation = )
public void outerTransaction() {
//Inserting text into a table“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("outerTransaction");
(tp);
//call (programming)innerTransactionmethodologies
TransactionPropagationService currentProxy = (TransactionPropagationService)();
();
//throw an exception
int i = 1 / 0 ;
}
@Transactional(propagation = )
public void innerTransaction() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction");
(tp);
}
outerTransaction() adds a transaction annotation with a propagation type ofREQUIRED
, since there was no transaction before, a new transaction is created and innerTransaction() is called, and innerTransaction() is also propagated with typeREQUIRED
, since there is a transaction in front of it, it joins the transaction, and finally the outerTransaction() throws an exception, and since both methods are in the same transaction, neither data will be inserted successfully. Let's test this.
Consistent with our expectations, innerTransaction() joins the transaction of outerTransaction(), and neither data will be inserted successfully after the exception is thrown.
SUPPORTS
If a transaction currently exists, it is added to the transaction, and if no transaction exists, it is executed in a non-transactional manner. Again we demonstrate this separately.
- If a transaction currently exists, join it with the following code:
@Transactional(propagation = )
public void outerTransaction() {
//Inserting text into a table“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("outerTransaction");
(tp);
//call (programming)innerTransactionmethodologies
TransactionPropagationService currentProxy = (TransactionPropagationService)();
();
//throw an exception
int i = 1 / 0 ;
}
@Transactional(propagation = )
public void innerTransaction() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction");
(tp);
}
The outerTransaction() is transactional and the innerTransaction() propagation type is:SUPPORTS
, then it will be added to the transaction, since the two methods are in the same transaction, after throwing an exception, both data will not be inserted successfully, let's test that the
As expected, no problems.
- If no transaction exists, it is executed in a non-transactional manner, as described in the following code:
public void outerTransaction() {
//Inserting text into a table“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("outerTransaction");
(tp);
//invocationsinnerTransactionmethodologies
TransactionPropagationService currentProxy = (TransactionPropagationService)();
();
}
@Transactional(propagation = )
public void innerTransaction() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction");
(tp);
//throw an exception
int i = 1 / 0 ;
}
We remove the transaction annotation from the outerTransaction() method, and the location of the thrown exception is moved to innerTransaction(), and since innerTransaction() has a propagation type ofSUPPORTS
The outer layer is not transactional, so innerTransaction() is also not transactional, and although it throws an exception, it doesn't roll back, and both data should be inserted successfully, so let's test it.
As expected, no problems.
MANDATORY
If a transaction currently exists, it is added to the transaction; if there is no transaction, an exception is thrown. Let's demonstrate each of these.
- If a transaction currently exists, it is added to the transaction with the following code:
@Transactional(propagation = )
public void outerTransaction() {
//Inserting text into a table“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("outerTransaction");
(tp);
//call (programming)innerTransactionmethodologies
TransactionPropagationService currentProxy = (TransactionPropagationService)();
();
//throw an exception
int i = 1 / 0 ;
}
@Transactional(propagation = )
public void innerTransaction() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction");
(tp);
}
The outerTransaction() method is transactional, and the innerTransaction() method is propagated with a type ofMANDATORY
, will be added to the transaction, and since the outerTransaction() method throws an exception, neither of the two pieces of data will succeed, so let's test that the
As expected, none of them worked.
- If there is currently no transaction, an exception is thrown with the following code:
public void outerTransaction() {
//Inserting text into a table“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("outerTransaction");
(tp);
//call (programming)innerTransactionmethodologies
TransactionPropagationService currentProxy = (TransactionPropagationService)();
();
//throw an exception
int i = 1 / 0 ;
}
@Transactional(propagation = )
public void innerTransaction() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction");
(tp);
}
We just removed the transaction annotation on outerTransaction(), let's see if it throws an exception, test that the
: No existing transaction found for transaction marked with propagation 'mandatory'
An exception was indeed thrown, and let's look at the database data again, the
The data insertion of the outerTransaction() method succeeds because the outerTransaction() method has no transactions, and the data is inserted successfully even though the later method throws an exception.
REQUIRES_NEW
Always creates a new transaction and hangs the current transaction if one currently exists. How to understand this statement? Let's look at the following code.
@Transactional(propagation = )
public void outerTransaction() {
//Inserting text into a table“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("outerTransaction");
(tp);
//call (programming)innerTransactionmethodologies
TransactionPropagationService currentProxy = (TransactionPropagationService)();
();
//throw an exception
int i = 1 / 0 ;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerTransaction() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction");
(tp);
}
outerTransaction() is transactional, and the innerTransaction() method's propagation type isREQUIRES_NEW
, a new transaction will be created, although outerTransaction() ends up throwing an exception, and since the two methods are two transactions, the exception will only be rolled back for the outer transaction, let's test that the
The innerTransaction() inserts the data successfully, and the outerTransaction() method rolls back because it has an exception. If the exception is moved from the outer layer to the inner layer, that is, the outer layer does not throw exceptions, while the inner layer throws exceptions, what will the execution result look like? Think about it yourself guys.
NOT_SUPPORTED
Execute the operation in a non-transactional way, and hang the current transaction if it exists. This propagation type indicates that the methods are non-transactional, regardless of whether there is a transaction in the outer layer or not, so let's look at the code first.
@Transactional(propagation = )
public void outerTransaction() {
//Inserting text into a table“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("outerTransaction");
(tp);
//invocationsinnerTransactionmethodologies
TransactionPropagationService currentProxy = (TransactionPropagationService)();
();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void innerTransaction() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction");
(tp);
//throw an exception
int i = 1 / 0 ;
}
outerTransaction() is transactional, and the propagation type of innerTransaction() isNOT_SUPPORTED
, stating that innerTransaction() executes as a non-transaction, the data is inserted, an exception is thrown, and since the outer layer is transacted, the outer transaction is rolled back, and we test that the
It's consistent with expectations, the inner layer executes with a non-transaction and the insertion of data is successful, the outer layer has a transaction and there is an exception so the transaction is rolled back.
NEVER
Execute the operation in a non-transactional manner and throw an exception if a transaction currently exists. Let's look specifically at the code that
@Transactional(propagation = )
public void outerTransaction() {
//Inserting text into a table“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("outerTransaction");
(tp);
//call (programming)innerTransactionmethodologies
TransactionPropagationService currentProxy = (TransactionPropagationService)();
();
}
@Transactional(propagation = )
public void innerTransaction() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction");
(tp);
//throw an exception
int i = 1 / 0 ;
}
outerTransaction() has a transaction, and the propagation type of innerTransaction() isNEVER
, since the outer method has a transaction, it has to throw an exception and the outer method has to rollback, so neither data will be inserted successfully, let's test that the
The exception thrown is:
: Existing transaction found for transaction marked with propagation 'never'
And look at the data in the database.
It's consistent with expectations.
NESTED
If a transaction currently exists, a new nested transaction is created within the current transaction; if there is no current transaction, a new task is created. Let's look at what each means.
- If there is no current transaction, a new task is created. This feels the same as REQUIRED, so let's look at the code for a moment.
public void outerTransaction() {
//Inserting text into a table“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("outerTransaction");
(tp);
//call (programming)innerTransactionmethodologies
TransactionPropagationService currentProxy = (TransactionPropagationService)();
();
}
@Transactional(propagation = )
public void innerTransaction() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction");
(tp);
//throw an exception
int i = 1 / 0 ;
}
There is no transaction for outerTransaction(), the propagation type of innerTransaction() isNESTED
, a new transaction will be created, the inner level will be rolled back because of the exception thrown, and the outer level, which has no transaction, will insert the data successfully, let's test this.
Consistent with expectations.
- If a transaction currently exists, a new nested transaction is created within the current transaction. Let's look at the code again.
@Transactional(propagation = )
public void outerTransaction() {
//Inserting text into a table“outerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("outerTransaction");
(tp);
//call (programming)innerTransactionmethodologies
TransactionPropagationService currentProxy = (TransactionPropagationService)();
try {
currentProxy.innerTransaction1();
currentProxy.innerTransaction2();
} catch (Exception e) {
();
}
}
@Transactional(propagation = )
public void innerTransaction1() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction1");
(tp);
}
@Transactional(propagation = )
public void innerTransaction2() {
//Inserting text into a table“innerTransaction”
TransactionPropagation tp = new TransactionPropagation();
("innerTransaction2");
(tp);
//throw an exception
int i = 1 / 0 ;
}
outerTransaction() has transactions that call innerTransaction1() and innerTransaction2(), respectively, and catch exceptions, and the propagation mechanisms for both innerTransaction1() and innerTransaction2() areNESTED
, an inner transaction will be created separately, innerTransaction1() ends normally without exception, innerTransaction2() throws an exception transaction rollback, and the outer layer will not rollback because the method can also end normally due to the caught exception. Our prediction is: outerTransaction() inserts successfully, innerTransaction1() inserts successfully, innerTransaction2() rolls back. Let's test that
It's consistent with our predictions.
summarize
To this point, Spring's seven propagation mechanism is introduced. Here is a lot of content, is not good to memorize, in fact, we do not have to memorize, look at the source code in the comments can be. If you can not, turn over my blog to see more it ~ ~