Let’s talk about the failure of the Spring annotation @Transactional | JD Cloud Technical Team

I. Introduction

emm, stepping on the pit again. The demand this time is mainly to optimize the demand tasks for overdue calculations, and the existing calculation tasks take too long to run. Briefly describe the problem this time: When performing operations on multiple databases in a project, what we expect is to encapsulate the entire transaction into a transaction, either all succeed or all fail. The status update of the first data is successful, but the insertion of the subsequent data is abnormal. After querying the data table, it is found that the status of the data has been successfully updated .

emmm, I checked the code and found that the @Transactional annotation was indeed used. I didn’t ask. So by querying the relevant information on the Internet, it was found that when using the transaction annotation @Transactional in Spring, the annotation will fail in several scenarios, that is, it cannot be encapsulated into a transaction operation as expected, so I studied the annotation and analyzed the relevant failure scenarios. , organize the article as follows;

2. @Transactional annotation failure scenario instance verification

1. @Transactional annotation attribute

Attributes type describe
value String An optional qualification descriptor specifying the transaction manager to use
propagation Enum:Propagation· Optional transaction propagation behavior settings
isolation Enum:Isolation Optional transaction isolation level settings
readOnly boolean Read-write or read-only transactions, default read-write
timeout int Transaction timeout setting
rollbackFor Array of Class objects, must inherit from Throwable An array of exception classes that caused the transaction to be rolled back
rollbackForClassName An array of class names, must inherit from Throwable An array of exception class names that caused the transaction to be rolled back
noRollbackFor Array of Class objects, must inherit from Throwable An array of exception classes that will not cause the transaction to be rolled back
noRollbackForClassName An array of class names, must inherit from Throwable An array of exception class names that will not cause transaction rollback

2, propagation attribute

propagation represents the propagation behavior of the transaction, the default value is Propagation.REQUIRED

Attributes describe
Propagation.REQUIRED Join the transaction if it currently exists, create a new transaction if it does not exist (default)
Propagation.SUPPORTS If there is a current transaction, join the transaction, if not, continue in a non-transactional manner
Propagation.MANDATORY If there is a current transaction, join the transaction, if not, throw an exception
Propagation.REQUIRES_NEW Re-create a new transaction, if there is a current transaction, the current transaction is tentative
Propagation.NOT_SUPPORTED Run in a non-transactional manner, if there is a current transaction, the current transaction is tentatively
Propagation.NEVER Run in a non-transactional manner, and throw an exception if there is a current transaction
Propagation.NESTED Same effect as Propagation.REQUIRED

3. What is the usage scenario of @Transactional annotation?

The @Transactional annotation can be applied to interfaces, classes, and class methods.

  • When used as a class , it means that all public methods of this class are configured with the same transaction attribute information.

  • When used as a method , when the class is configured with the @Transactional annotation and the method is also configured with @Transactional, the transaction of the method will override the transaction configuration information of the class.

  • When used as an interface , it is not recommended, because using @Transactional on the interface and configuring Spring AOP to use CGLib dynamic proxy will cause it to fail.

4. @Transactional annotation failure scenario?

  • The @Transactional annotation will be invalid when applied to non-public modified methods.

Reason for failure: In Spring AOP proxy, TransactionInterceptor (transaction interceptor) intercepts before and after the execution of the target method, and the Intercept method of DynamicAdvisedInterceptor (internal class of CglibAopProxy) or the invoke method of JDKDynamicAopProxy will indirectly call the computeTransactionAttribute method of AbstractFallbackTransationAttributeSource to obtain the @Transactional annotation The transaction configuration information.

1 protected TransactionAttribute computeTransactionAttribute(Method method,
2    Class<?> targetClass) {
3        // Don't allow no-public methods as required.
4        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
5        return null;
6    }


This method will check whether the modifier of the target method is public , and the attribute configuration information of @transactional will not be obtained in the non-public scope. Among them, the @Transactional annotation is used on the protected and private modified methods , and the transaction will be invalid but no error will be reported.

  • @Transactional annotation attribute propagation setting error causes the annotation to fail

Failure reason: configuration error, the three transaction propagation methods of PROPAGATION_SUPPORTS, PROPAGATION_NOT_SUPPORTED, and PROPAGATION_NEVER will not roll back.

▪ Example verification: wrote a demo for testing. The main functions of the demo are as follows: perform two database insert operations, and add notes in the extended information field;

▪ The running results are as follows. The constructed order number does not exist and the order query is empty, triggering an exception. Observing the database, it is found that the first database insertion operation has been successfully executed , so the verification of the @Transactional annotation is invalid ;

  • The @Transactional annotation attribute rollbackFor is set incorrectly, causing the annotation to fail

rollbackFor can specify the type of exception that can trigger transaction rollback. By default, Spring throws an unchecked exception (inherited from RuntimeException) or Error to roll back the transaction. If other types of exceptions are thrown in the transaction, but you expect Spring to roll back the transaction, you need to specify the rollbackFor attribute, otherwise it will fail.

  • Method calls in the same class cause @Transactional to fail

For example, there are methods A and B in the class demo, and the @Transactional annotation is used in method B. Method A has no annotation, but the demo class calls method B through method A. Such indirect calls will cause the @Transactional transaction annotation in method B to fail.

Reason for failure : only when the transaction method is called by code other than the current class, there will be proxy object management generated by Spring. (caused by the Spring AOP proxy mechanism).

▪Example verification : The construction scene in the demo is in the same class, add the @Transactional annotation to the test method, do not add this annotation to the querRiskScore method, and then call the test method in the querRiskScore method; observe whether multiple insertion operations will be caused by The rollback is interrupted due to an exception;


▪ The running results are as follows. The constructed order number does not exist and the order query is empty to trigger an exception. Observing the database, it is found that the first database insertion operation has been successfully executed , but the second data insertion operation failed , and no transaction operation was triggered due to the exception. , so the call between verification @Transactional annotation methods will be invalid ;

  • Multi-threaded tasks can cause @Transaction cases to fail

Cause of failure : Threads are not managed by Spring, so threads cannot use Spring transactions by default, nor can they obtain Spring-injected beans. Multi-threading is enabled in methods managed by Spring's declarative transaction management, and methods in multi-threads are not controlled by transactions. .

  • The exception is caught by the catch in the method, causing @Transactional to fail

For example, if an exception is thrown inside method B, and method A try-catches the exception of method B at this time, the transaction cannot be rolled back normally.

Reason for failure : Because after the exception is thrown in method B, the current transaction needs to be rolled back, but in method A, because you manually capture the exception and process it, method A thinks that the current transaction should be committed normally, and there will be inconsistencies at this time. Throw org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only exception.

▪Instance verification : The essence of this scenario is that the exception is caught and cannot be thrown normally, which leads to the @Transactional annotation not working properly. I simplified the demo instance scenario and constructed the scenario as follows: add the @Transactional annotation to the querRiskScore method, and then Catch exceptions in the querRiskScore method; observe whether multiple insert operations will interrupt the rollback due to exceptions;


▪ The running results are as follows. The constructed order number does not exist and the order query is empty and an exception is triggered. However, we capture the exception inside the method and do not throw it to the upper layer. The scenario we expect is that the execution of data insertion fails twice. However, observing the database found that the first database insertion operation has been successfully executed , and the second data insertion operation has been successfully executed , which is inconsistent with our expected results, so verify that the @Transactional annotation will fail in the scene where the exception is caught in the method ;

The reason : Spring's transaction is started before the business method is called, and the commit or rollback is executed after the business method is executed. Whether the transaction is executed depends on whether a runtime exception is thrown. If a runtime exception is thrown and in your business method If there is no catch, the transaction will be rolled back.

3. Review of "business" knowledge

1. What is a transaction?

Transaction (Transaction) is a program execution logic unit (Unit) composed of a series of operations to access and update data in the system.

Usually we refer to the transaction refers to the database transaction, using the database transaction has the following two advantages:

  • When multiple applications access the database concurrently, transactions can provide an isolation method between these applications to prevent each other's operations from interfering with each other .

  • Transactions provide a way for a sequence of database operations to recover from a failure to a normal state, and at the same time provide a way for the database to maintain data consistency even in an abnormal state .

2. What are the characteristics of a transaction?

Atomicity, Consistency, Isolation, and Persistence, referred to as the ACID characteristics of transactions.

  • atomicity

The atomicity of a transaction means that a transaction must be an atomic operation sequence unit, that is, each operation contained in a transaction will only appear in two states during one execution: all are successfully executed, and none are executed. The failure of any operation will cause the failure of the entire transaction, and other operations that have been executed will be undone and rolled back. Only when all operations are successful, the entire transaction is considered to be successfully completed.

  • consistency

The consistency of the transaction means that the execution of the transaction cannot destroy the integrity and consistency of the database data. Before and after the execution of a transaction, the database must be in a consistent state. That is, the result of transaction execution must be to transition the database from one consistent state to another, so a database is said to be in a consistent state when it contains only the results of successful transaction commits. And if the database system fails during operation, some transactions are forced to be interrupted before they are completed, and some of the modifications made by these unfinished transactions to the database have been written into the physical database, and the database is in an incorrect state. , or an inconsistent state.

  • isolation

Transaction isolation means that in a concurrent environment, concurrent transactions are isolated from each other, and the execution of a transaction cannot be interfered by other transactions. That is to say, when different transactions manipulate the same data concurrently, each transaction has its own complete data space, that is, the operations and data used within a transaction are isolated from other concurrent transactions, and the transactions between concurrently executed transactions Cannot interfere with each other.

  • Persistence

Once a transaction is committed, its modifications will be permanently saved in the database, even if the database fails, it should not have any impact on it. It should be noted that the durability of the transaction cannot be 100% durable, and can only be guaranteed from the perspective of the transaction itself, and some external reasons cause the database to fail, such as hard disk damage, then all submitted data may be lost .

3. What is a transaction in Spring?

Spring also provides a good transaction management mechanism, mainly divided into programmatic transactions and declarative transactions .

  • programmatic transaction

Refers to the manual management of transaction submission, rollback and other operations in the code, and the code is relatively intrusive. The programmatic transaction method requires developers to manually manage transactions such as opening, committing, and rolling back in the code.

public void test() {

      TransactionDefinition def = new DefaultTransactionDefinition();

      TransactionStatus status = transactionManager.getTransaction(def);

      try {

         // 事务操作

         // 事务提交

         transactionManager.commit(status);

      } catch (DataAccessException e) {

         // 事务提交

         transactionManager.rollback(status);

         throw e;

      }

}


  • declarative transaction

Declarative transactions are aspect-oriented based on AOP, which decouples specific business and transaction processing, and the code is very low intrusive, so it is commonly used in actual development. We often use the xml configuration file method of TX and AOP and the @Transactional annotation method.

▪Advantages of declarative transactions:

It is non-invasive to the code, and only needs to write business logic in the method, saving a lot of code.

▪Disadvantages of declarative transactions:

1. The granularity of declarative transactions: The limitation of declarative transactions is that the minimum granularity must be applied to methods , and it is not suitable for long-term and high-concurrency scenarios.

2. Declarative transactions are easily overlooked by developers. When there are RPC remote calls, MQ sending, Redis update, file writing and other operations in the transaction nesting method, the following scenarios may exist:

▪ In the method of transaction nesting, the RPC call is successful, but the local transaction rollback causes the RPC call to fail to roll back (distributed transactions are not discussed yet).

▪Remote calls in the method of transaction nesting will prolong the entire transaction cycle, resulting in the consistent occupation of the database connection of the transaction, and too many similar operations will cause the database connection pool to be exhausted.

3. The wrong use of declarative transactions will lead to failure in some scenarios .

Four. Summary

Author: Jingdong Technology Song Huichao

Source: JD Cloud Developer Community

RustDesk 1.2: Using Flutter to rewrite the desktop version, supporting Wayland's alleged GPT-4 model architecture leak: Contains 1.8 trillion parameters, using a mixed expert model (MoE) Musk announced the establishment of xAI company deepin V23 successfully adapted WSL CentOS project claims " Open to all" Rust 1.71.0 Stable Release React Is it having an Angular.js moment? Microsoft launches a new default font, Aptos, to replace CalibriMicrosoft : Increase efforts to use Rust IntelliJ IDEA 2023.1.4 release on Windows 11
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/10089156