Perfectly solve the problem that spring declarative transactions are not rolled back
The @ transactional annotation is added to the service as usual. Why is there data inconsistency when querying the database? Think about it. It must be that the transaction does not work and the data is not rolled back when exceptions occur. So I tested the relevant code, and found that I stepped into two pits. It is really the data inconsistency caused by the non rollback of the transaction.
Here are some lessons learned:
Management and operation methods of spring transactions
Programmed transaction management
It is rarely used in practical applications
Manually manage transactions by using the transactiontemplate
Declarative transaction management
Recommended in development (least code intrusion)
Spring's declarative transactions are implemented through AOP
Mainly master declarative transaction management.
Two reasons why spring transactions are not rolled back
To sum up, there are two reasons why transactions are not rolled back. One is the internal method call of service class, and the other is try Catch exception.
1. Internal method call of service class
There is probably a method a in the service that will call method B internally. Method a does not have transaction management. Method B adopts declarative transactions and does transaction management by declaring transactional annotations on the method. The example code is as follows:
Unit test codes are as follows:
As can be seen from the previous section, declarative transactions are implemented through AOP dynamic agents. In this way, an agent class will be generated for transaction management, and the target class itself cannot perceive the existence of the agent class.
For the method annotated with @ transactional, when calling the method of the proxy class, the transaction will be started through the interceptor transactioninterceptor, and then the method of the target class will be called. Finally, after the call, the transactioninterceptor will commit or roll back the transaction. The approximate flow is as follows:
To sum up, calling method B in method A is actually through the reference of "this", that is, directly calling the method of the target class, instead of the proxy class obtained through the Spring context, so the transaction will not be opened.
2. try... Catch exception
In a section of business logic, the database exception is handled, and try is used The catch clause catches an exception and throws a custom exception, which causes the transaction not to be rolled back. The example code is as follows:
Bizexception is defined as follows:
When an exception occurs to the declarative transaction in the above code, the transaction will not be rolled back. Although I caught an exception in the code, I also threw an exception. Why didn't the transaction roll back? The guess is that the exception type is wrong, so I began to query the reason, looked through the official spring documentation and found the answer. The following is a translation from the spring official website.
17.5. 3 rollback of declarative transactions
The previous section describes how to set and enable spring transactions, which are generally set in the service layer code of your application. This section will introduce how to control transaction rollback in simple and popular declarative transactions.
The recommended transaction rollback method in the transaction framework of spring framework is to throw an exception in the current transaction context. If the exception is not handled, when the exception call stack is thrown, the transaction framework code of spring framework will catch any unhandled exception and decide whether to mark the transaction as rollback.
In the default configuration, the transaction framework code of spring framework will only mark the transactions with runtime and unchecked exceptions as rollback; That is, the exception thrown in the transaction is a runtimeException or its subclass, so that the transaction will be rolled back (error will also cause the transaction to roll back by default). Under the default configuration, all checked exceptions will not cause the transaction to roll back.
Note: unchecked exception includes error and runtimeException All subclasses of runtimeException also belong to this class. The other is checked exception.
You can accurately configure the exception type and specify the transaction rollback of this exception type, including checked exceptions. The following XML code fragment shows how to configure a checked exception to cause transaction rollback and apply a custom exception type:
Notes with the same effect are as follows:
When you encounter an exception and don't want to roll back the transaction, you can also specify the rule not to roll back. The following example tells you that even when you encounter an unhandled instrumentnotfoundexception exception, the transaction framework of spring framework will commit the transaction without rolling back.
Notes with the same effect are as follows:
There is also a more flexible rollback rule configuration method, which specifies what exceptions are rolled back and what exceptions are not rolled back. When the transaction framework of spring framework catches an exception, it will match the configured rollback rules to decide whether to mark the rollback transaction, and use the rule result with the strongest matching degree. Therefore, the following configuration example means that any exception except instrumentnotfoundexception will cause transaction rollback.
You can also roll back a transaction programmatically. Although the method is very simple, it also has strong code intrusion, which makes your business code closely bound with the transaction framework code of spring framework. The example code is as follows:
If possible, it is strongly recommended that you use declarative transaction to rollback transactions. For programmatic transactions, you can also use it if you strongly need it, but its usage flies in the face of achieving a clean POJO based architecture (I don't understand...)
After reading this section of the official document, we found the answer to the question. It turns out that our custom exception is not a runtimeException. My solution is to add rollbackfor = {bizexception. Class} in the annotation @ transactional. You may ask me why I don't modify the custom exception to inherit runtimeException, because I need bizexception to be a checked exception.
The above article perfectly solves the problem of non rollback of spring declarative transactions, which is all the content shared by Xiaobian. I hope it can give you a reference and support more programming tips.