Location>code7788 >text

Spring transaction propagation mechanism (the most complete example)

Popularity:934 ℃/2024-09-24 14:38:54

When we are developing with the Spring Framework, we often use theservicelayer writes a lot of methods, and they are all methods with the(political, economic etc) affairss, 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 / 0Throw an exception.

methodologiesinnerTransaction()Insert the text "innerTransaction" into the table by calculating the1 / 0Throw 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

REQUIREDis 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:

  1. 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.

  1. 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.

  1. 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.

  1. 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 ofSUPPORTSThe 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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 ~ ~