In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly shows you the "@ Transactional annotation does not work", the content is easy to understand, clear, hope to help you solve your doubts, the following let the editor lead you to study and learn the "@ Transactional annotation does not work" this article.
Introduction to the first Transactional failure scenario
When the Transactional annotation annotation method modifier is non-public, the @ Transactional annotation will not work. For example, the following code.
Define an incorrect @ Transactional annotation implementation and modify the method of a default accessor
/ * @ author zhoujy * @ date December 06, 2018 * * / @ Componentpublic class TestServiceImpl {@ Resource TestMapper testMapper; @ Transactional void insertTestWrongModifier () {int re = testMapper.insert (new Test (10je 20je 30)); if (re > 0) {throw new NeedToInterceptException ("need intercept");} testMapper.insert (new Test (210J 20J 30));}}
In the same package, create a new calling object for access.
@ Componentpublic class InvokcationService {@ Resource private TestServiceImpl testService; public void invokeInsertTestWrongModifier () {/ / call the default accessor method of the @ Transactional tag testService.insertTestWrongModifier ();}}
Test case
RunWith (SpringRunner.class) @ SpringBootTestpublic class DemoApplicationTests {@ Resource InvokcationService invokcationService; @ Testpublic void testInvoke () {invokcationService.invokeInsertTestWrongModifier ();}}
As a result of the above access method, the transaction is not opened, so when the method throws an exception, the testMapper.insert (new Test); operation will not be rolled back. If the TestServiceImpl#insertTestWrongModifier method is changed to public, the transaction will be started normally, testMapper.insert (new Test (1010 20) 30); will be rolled back.
The second kind
The method that invokes the @ Transactional annotation inside the class is called inside the class. In this case, it can also cause the transaction to not open. The sample code is as follows.
Set up an internal call
/ * * @ author zhoujy * @ date December 06, 2018 * * / @ Componentpublic class TestServiceImpl implements TestService {@ Resource TestMapper testMapper; @ Transactional public void insertTestInnerInvoke () {/ / the transaction method of the normal public modifier int re = testMapper.insert (new Test); if (re > 0) {throw new NeedToInterceptException ("need intercept") } testMapper.insert (new Test (210 and 20));} public void testInnerInvoke () {/ / the @ Transactional tagged method is called inside the class. InsertTestInnerInvoke ();}}
Test cases.
@ RunWith (SpringRunner.class) @ SpringBootTestpublic class DemoApplicationTests {@ Resource TestServiceImpl testService; / * Test @ Transactional tagging method * / @ Testpublic void testInnerInvoke () {/ / Test whether the external call transaction method is normal / / testService.insertTestInnerInvoke (); / / Test whether the internal call transaction method is normal testService.testInnerInvoke ();}}
The above is the test code used. Running the test knows that the external call transaction method can start the transaction, and the testMapper.insert (new Test) operation will be rolled back.
Then run another test case and call a method inside the class to call the transaction method marked by @ Transactional. The result of the run is that the transaction will not open normally, and the testMapper.insert (new Test) operation will be saved to the database without rollback.
The third kind
The exception was caught inside the transaction method, and no new exception was thrown, causing the transaction operation not to be rolled back. The sample code is as follows.
/ * @ author zhoujy * @ date Dec 06, 2018 * * / @ Componentpublic class TestServiceImpl implements TestService {@ Resource TestMapper testMapper; @ Transactional public void insertTestCatchException () {try {int re = testMapper.insert (new Test (10Ling 20m 30)); if (re > 0) {/ / throw exception throw new NeedToInterceptException ("need intercept") during operation } testMapper.insert (new Test (210 and 20));} catch (Exception e) {System.out.println ("i catch exception");}
The test case code is as follows.
RunWith (SpringRunner.class) @ SpringBootTestpublic class DemoApplicationTests {@ Resource TestServiceImpl testService; @ Testpublic void testCatchException () {testService.insertTestCatchException ();}}
Running the test case found that although an exception was thrown, the exception was caught, not thrown out of the method, and the testMapper.insert (new Test (210-20)) operation was not rolled back.
The above three are the main reasons why the @ Transactional annotation does not work and the @ Transactional annotation fails. Let's use the @ Transactional annotation in spring to analyze why the @ Transactional annotation doesn't work.
Analysis of the principle that @ Transactional annotations don't work
First of all, if you don't understand the principle of @ Transactional annotation implementation, you can take a look at another article, @ Transactional annotation implementation principle, and then start to analyze the following three situations with source code.
First kind
When the @ Transactional annotation annotation method modifier is non-public, the @ Transactional annotation will not work. The reason analyzed here is that @ Transactional is implemented based on dynamic proxy, and the implementation method is analyzed in the implementation principle of @ Transactional annotation. In the process of bean initialization, a proxy object is created for bean instances with @ Transactional annotation. There is a process of spring scanning @ Transactional annotation information. Unfortunately, it is reflected in the source code. If the @ Transactional method is marked with a modifier other than public, then the @ Transactional information of the default method is empty. Then there will be no proxy object creation for bean or proxy calls for methods.
The principle of @ Transactional annotation implementation describes how to determine whether a bean creates a proxy object. The logic is roughly. Create an aop pointcut BeanFactoryTransactionAttributeSourceAdvisor instance based on spring, and traverse the class method object of the current bean to determine whether the annotation information above the method contains @ Transactional. If any bean method contains @ Transactional annotation information, the BeanFactoryTransactionAttributeSourceAdvisor pointcut is adapted. Then we need to create a proxy object, and then the proxy logic manages the transaction opening and closing logic for us.
In the spring source code, when intercepting the creation process of bean and finding the pointcut for bean adaptation, the following method is used to find the @ Transactional information above the method. If there is any, it means that the pointcut BeanFactoryTransactionAttributeSourceAdvisor can be applied (canApply) to bean.
AopUtils#canApply (org.springframework.aop.Pointcut, java.lang.Class, boolean)
Public static boolean canApply (Pointcut pc, Class targetClass, boolean hasIntroductions) {Assert.notNull (pc, "Pointcut must not be null"); if (! pc.getClassFilter (). Matches (targetClass)) {return false;} MethodMatcher methodMatcher = pc.getMethodMatcher (); if (methodMatcher = = MethodMatcher.TRUE) {/ / No need to iterate the methods if we're matching any method anyway... Return true;} IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;} / / traversing the method object Set > (ClassUtils.getAllInterfacesForClassAsSet (targetClass)); classes.add (targetClass); for (Class clazz: classes) {Method [] methods = ReflectionUtils.getAllDeclaredMethods (clazz) For (Method method: methods) {if ((introductionAwareMethodMatcher! = null & & introductionAwareMethodMatcher.matches (method, targetClass, hasIntroductions)) | | / / the @ Transactional annotation information on the query method methodMatcher.matches (method, targetClass) {return true;}} return false;}
We can break the point in the above method, debug the trace code step by step, and eventually the above code will call the following method to determine. At the breakpoint on the following method, looking back at the method call stack is also a good way to trace.
AbstractFallbackTransactionAttributeSource#getTransactionAttribute
AbstractFallbackTransactionAttributeSource#computeTransactionAttribute
Protected TransactionAttribute computeTransactionAttribute (Method method, Class targetClass) {/ / Don't allow no-public methods as required. / / non-public method. The @ Transactional information returned is omitted after null if (allowPublicMethodsOnly () & &! Modifier.isPublic (method.getModifiers () {return null;} / /. } do not create a proxy object
Therefore, if the modifiers on all methods are non-public, the proxy object will not be created. Take the initial test code as an example, if the testService of the normal modifier is the proxy object created by cglib in the image below.
If all the methods in class are non-public, then they will not be proxy objects.
Do not make proxy calls
Consider a situation, as shown in the following code. Both methods are annotated with the @ Transactional annotation, but one has the public modifier and the other does not, so we can foresee that a proxy object will be created because there is at least one @ Transactional annotation method with the public modifier.
When a proxy object is created, will insertTestWrongModifier start the transaction? The answer is no.
/ * @ author zhoujy * @ date December 06, 2018 * * / @ Componentpublic class TestServiceImpl implements TestService {@ Resource TestMapper testMapper; @ Override @ Transactional public void insertTest () {int re = testMapper.insert (new Test); if (re > 0) {throw new NeedToInterceptException ("need intercept");} testMapper.insert (new Test (210) } @ Transactional void insertTestWrongModifier () {int re = testMapper.insert (new Test); if (re > 0) {throw new NeedToInterceptException ("need intercept");} testMapper.insert (new Test (210));}}
The reason is that when the dynamic proxy object makes proxy logic calls, the CglibAopProxy.DynamicAdvisedInterceptor#intercept in the intercepting function of the proxy object created by cglib has the following logic, which aims to obtain the method-adapted aop logic of the current proxy object.
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice (method, targetClass)
Similarly, the process of finding aop logic for @ Transactional annotations is also performed once.
AbstractFallbackTransactionAttributeSource#getTransactionAttribute
AbstractFallbackTransactionAttributeSource#computeTransactionAttribute
In other words, you also need to find a @ Transactional annotation on the method. Without it, the agent logic corresponding to the agent @ Transactional will not be executed, and the method will be executed directly. Without the @ Transactional annotation agent logic, you can't start a transaction, which is what we talked about in the previous article.
The second kind
The method that invokes the @ Transactional annotation inside the class is called inside the class. In this case, it can also cause the transaction to not open.
After a detailed analysis of the first, we should be able to guess why transaction management is not enabled in this situation.
Since transaction management is based on the proxy logic of the dynamic proxy object, if the transaction method within the class is called within the class, the process of calling the transaction method is not called through the proxy object, but directly through the this object.
In fact, we can play like this, internal calls can also open transactions, the code is as follows.
/ * @ author zhoujy * @ date December 06, 2018 * * / @ Componentpublic class TestServiceImpl implements TestService {@ Resource TestMapper testMapper; @ Resource TestServiceImpl testServiceImpl; @ Transactional public void insertTestInnerInvoke () {int re = testMapper.insert (new Test); if (re > 0) {throw new NeedToInterceptException ("need intercept");} testMapper.insert (new Test (210) } public void testInnerInvoke () {/ / internally call the transaction method testServiceImpl.insertTestInnerInvoke ();}}
The above is the use of proxy objects for transaction calls, so transaction management can be turned on, but in practice, no one will play like this.
The third kind
The exception was caught inside the transaction method, and no new exception was thrown, causing the transaction operation not to be rolled back.
In this case, we may be more common, and the problem lies in the agent logic. Let's first take a look at how the dynamic agent logic in the source code manages affairs for us. I will not elaborate on this process, and you can refer to other articles.
TransactionAspectSupport#invokeWithinTransaction
The code is as follows.
Protected Object invokeWithinTransaction (Method method, Class targetClass, final InvocationCallback invocation) throws Throwable {/ / If the transaction attribute is null, the method is non-transactional. Final TransactionAttribute txAttr = getTransactionAttributeSource (). GetTransactionAttribute (method, targetClass); final PlatformTransactionManager tm = determineTransactionManager (txAttr); final String joinpointIdentification = methodIdentification (method, targetClass); if (txAttr = = null | |! (tm instanceof CallbackPreferringPlatformTransactionManager)) {/ / Standard transaction demarcation with getTransaction and commit/rollback calls. / / Open transaction TransactionInfo txInfo = createTransactionIfNecessary (tm, txAttr, joinpointIdentification); Object retVal = null; try {/ / This is an around advice: Invoke the next interceptor in the chain. / / This will normally result in a target object being invoked. / / reflection calls business method retVal = invocation.proceedWithInvocation ();} catch (Throwable ex) {/ / target invocation exception / / rollback transaction completeTransactionAfterThrowing (txInfo, ex) in catch logic; throw ex;} finally {cleanupTransactionInfo (txInfo);} / / commit transaction commitTransactionAfterReturning (txInfo); return retVal } else {/ /. }} above is all the content of the article "Why the @ Transactional annotations don't work". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.