Resolve the problem that @ Transactional annotation transaction does not work without rollback

  • 2021-08-28 20:07:52
  • OfStack

These days, I found in the project that I used @ Transactional to annotate transactions, and threw an exception without rolling back. Finally found out the reason.

If this happens to you, you can start checking from below.

1. Characteristics

Let's first understand the characteristics of @ Transactional annotation transaction under 1, which can better troubleshoot problems

1. Adding @ Transactional to the service class label (generally not recommended on the interface) can bring the whole class into spring transaction management, and one transaction will be opened when each business method is executed, but these transactions are managed in the same way.

2. @ Transactional annotations can only be applied to public visibility methods. If applied to protected, private, or package visibility methods, no error will be reported, but transaction settings will not work.

3. By default, Spring will carry out transaction rollback on unchecked exception; If it is an checked exception, it will not be rolled back.

What is checked anomaly and what is unchecked anomaly

In java, exceptions derived from Error or RuntimeException (such as null pointer, 1/0) are called unchecked exceptions, and other exceptions inherited from java. lang. Exception are collectively called Checked Exception, such as IOException, TimeoutException, etc.

Spicy and popular 1 point:

If you write code, such as null pointer anomaly, it will be rolled back. If the file reads and writes, and the network goes wrong, spring will not be rolled back. Then I'll teach you how to remember this, because many students are easy to confuse. When you write code, some IOException can be detected by our compiler, which is called checked exception. When you write code, the null pointer can't be detected, so it is called unchecked exception. Isn't this easy to remember

4. Read-only transactions:


@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

The read-only flag is applied only when the transaction starts, otherwise even the configuration will be ignored.

Starting a transaction increases thread overhead and the database is locked due to shared reads (depending on the database type and transaction isolation level). Typically, you do not have to set up a read-only transaction to add extra overhead when reading data only.

2: Transaction Propagation Pattern

Propagation enumerates a variety of transaction propagation patterns, some of which are listed as follows:

1. REQUIRED (default mode): Business methods need to run in one container. If the method is already in a transaction when it runs, join the transaction; Otherwise, create a new transaction yourself.

2. NOT_SUPPORTED: Declare a method without requiring a transaction. If the method is not associated with a transaction, the container does not open a transaction for it. If the method is called in a transaction, the transaction is suspended, and the original transaction resumes execution after the call.

3. REQUIRESNEW: This method aggregates a new transaction for itself, regardless of whether a transaction exists or not. If the method is already running in 1 transaction, the original transaction is suspended and a new transaction is created.

4. MANDATORY: This method can only be executed in one existing transaction, and the business method cannot initiate its own transaction. If it is called in an environment without transactions, the container throws an exception.

5. SUPPORTS: If the method is called within the scope of a transaction, the method becomes part 1 of the transaction. If a method is called outside the scope of the transaction, the method executes without a transaction.

6. NEVER: This method must not be executed within the scope of transactions. If you are, leave an exception. This method executes normally only if it is not associated with any transactions.

7. NESTED: If an active transaction exists, it runs in a nested transaction. If there is no active transaction, it is executed according to the REQUIRED property. It uses a single transaction, which has multiple savepoints that can be rolled back. Rollback of internal transactions does not affect external transactions. It only works on the DataSourceTransactionManager transaction manager.

3: Resolve the problem of Transactional annotation not rolling back

1. Check if your method is public

2. Is your exception type an unchecked exception

What if I want to roll back the check exception as well, just note the exception type above


@Transactional(rollbackFor=Exception.class) 

Similarly, there is norollbackFor, which customizes exceptions that do not roll back

3. The database engine should support transactions. If it is MySQL, note that the table should use an engine that supports transactions, such as innodb. If it is myisam, transactions will not work

4. Is the annotation parsing enabled


<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

5. Does spring scan your package? The following is the package scanned to org. test


<context:component-scan base-package="org.test" ></context:component-scan>

6. Check whether it is a method call in the same class (for example, a method calls b method in the same class)

7. Is the anomaly caught by your catch

Above, add it later ~

Add: Some notes on @ Transactional transactions

There are several points that need everyone's attention:

A. 1 Whether a function is transactional or not must be taken into consideration in design and coding. You can't just complete the basic functions of ok.

B. If a transaction is added, the development environment must be tested well (the test environment also triggers exceptions and tests rollback as much as possible) to ensure that the transaction takes effect.

C. Please pay attention to the following considerations for transaction usage.

1. Do not declare @ Transactional on the interface, but use the @ Transactional annotation on the methods of the concrete class, otherwise the annotation may be invalid.

2. Don't try to save trouble. Placing @ Transactional in a class-level declaration will make all methods transactional. Therefore, @ Transactional should be placed at the method level, without using transactional methods, so do not place transactions, such as query methods. Otherwise, there is an impact on performance.

3. Using @ Transactional method, @ Transactional is invalid for method calls in the same class. For example, there is a class Test with one of its methods A, and A calls Test's method B (whether B is public or private), but A does not declare annotation transactions, while B does. The transaction of B will not work after A is called externally. (Often makes mistakes here)

4. Methods using @ Transactional can only be public, and methods annotated by @ Transactional are valid only when they are called by other external classes, so they can only be public. The truth is related to the above. So if you use the @ Transactional annotation on protected, private, or package-visible method, it will not report an error, but the transaction is invalid.

5. After testing in ICORE-CLAIM, the results are as follows:

A. Throws the checked exception XXXException and the transaction rolls back.

B. The runtime exception NullPointerException is thrown and the transaction rolls back.

In C. Quartz, execute directly calls and adds @ Transactional method, which can be rolled back; Indirect call, no rollback. (i.e. mentioned in point 3 above)

D. In asynchronous task, execute directly calls and adds @ Transactional method, which can be rolled back; Indirect call, no rollback. (i.e. mentioned in point 3 above)

E. Adding @ Transactional to action does not roll back. Remember not to add transactions to action.

F. Add @ Transactional to service. If action calls this method directly, it will roll back, if it is indirect, it will not roll back. (i.e. mentioned in 3 above)

G. Add @ Transactional to private in service and the transaction will not be rolled back.


Related articles: