@Transactional失效场景演示与总结

531 阅读2分钟

1. 方法为非 public 修饰

image.png

看下数据库

image.png

原因就得看事务的源码了,他会校验是否为public如果不是public根本就不会解析@Transactional。

image.png

2. 同一个类中方法调用,导致@Transactional失效 (常见的坑)

  • saveA没有事务注解,saveA2有事务注解,saveA中调用saveA2使用this调用方式,saveA2上的事务注解将会失效

原因: Spring在扫描Bean的时候会自动为标注了@Transactional注解的类生成一个代理类(proxy),当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作,但是同类中的方法互相调用,相当于this.saveA2(),此时的saveA2方法并非是代理类调用,而是直接通过原有的Bean直接调用,所以注解会失效

注意(其实就算不显示使用this,在编译后也是this调用(未编译代码中是没有this的))如下图所示

image.png

看下现象: image.png

数据库: image.png

咋样?是不是saveA2事务没生效?哈哈

但是我就是需要在同类中调用,并且想让他生效怎么办???

使用 ((TransactionalInvalidationImpl) AopContext.currentProxy()).saveA2();

假如以上代码后启动 。。。。 我去报错了:

image.png

java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.

	at org.springframework.aop.framework.AopContext.currentProxy(AopContext.java:69)
	at com.xzll.test.service.impl.TransactionalInvalidationImpl.saveA(TransactionalInvalidationImpl.java:31)
	at com.xzll.test.service.impl.TransactionalInvalidationImpl$$FastClassBySpringCGLIB$$8c5749ef.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:684)
	at com.xzll.test.service.impl.TransactionalInvalidationImpl$$EnhancerBySpringCGLIB$$b4a8c905.saveA(<generated>)
	at com.xzll.test.transactional.TransactionalInvalidationTest.test(TransactionalInvalidationTest.java:25)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
	at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
  • 解决: 添加 @EnableAspectJAutoProxy(exposeProxy = true)//exposeProxy 直译为 暴露代理

image.png

看下数据库 可以看到saveA2没有保存成功,只有saveA这个数据保存成功了。可以知道saveA2的事务生效了。

image.png

  • 其实还有个解决方式(本质和EnableAspectJAutoProxy一样) 就是修改bean定义信息 即实现 BeanDefinitionRegistryPostProcessor (偷偷告诉你,@EnableAspectJAutoProxy(exposeProxy = true)内部就是这么做的 哈哈)

注意此时我们已经把 @EnableAspectJAutoProxy注释掉了。

image.png

可以看到事务生效,saveA2的数据没有被保存而是回滚了。

image.png


  • 注意 如果调用方使用了事务那么就算使用this调用同类方法,事务也是会生效的,其实生效的不是saveA2 而是saveA

看下现象: image.png

如图,事务回滚,数据没插入成 image.png

3. 事务参数设置错误

如:

4. catch(吞异常) 也会导致失效 具体就不演示了。

另外推荐一篇从源码层面讲解的比较有深度的文章 戳这里即可