1、spring的事务注解@Transactional只能放在public修饰的方法上才起作用,如果放在其他非public(private,protected)方法上,虽然不报错,但是事务不起作用
2、如果采用spring+spring mvc,则context:component-scan重复扫描问题可能会引起事务失败。
如果spring和mvc的配置文件中都扫描了service层,那么事务就会失效。
原因:因为按照spring配置文件的加载顺序来讲,先加载springmvc配置文件,再加载spring配置文件,我们的事物一般都在srping配置文件中进行配置,如果此时在加载srpingMVC配置文件的时候,把servlce也给注册了,但是此时事物还没加载,也就导致后面的事物无法成功注入到service中。所以把对service的扫描放在spring配置文件中或是其他配置文件中。
3、如使用mysql且引擎是MyISAM,则事务会不起作用,原因是MyISAM不支持事务,可以改成InnoDB引擎
4、在业务代码中如果抛出RuntimeException异常,事务回滚;但是抛出Exception,事务不回滚;
解决方法@Transactional改为@Transactional(rollbackFor = Exception.class)
5、如果在加有事务的方法内,使用了try...catch..语句块对异常进行了捕获,而catch语句块没有throw new RuntimeExecption异常,事务也不会回滚
6、在类A里面有方法a 和方法b, 然后方法b上面用 @Transactional加了方法级别的事务,在方法a里面 调用了方法b, 方法b里面的事务不会生效。原因是在同一个类之中,方法互相调用,切面无效 ,而不仅仅是事务。这里事务之所以无效,是因为spring的事务是通过aop实现的。
注意事项
1.在需要事务管理的地方加@Transactional 注解。@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上 。
2.@Transactional 注解只能应用到 public 可见度的方法上 。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
3.注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据。必须在配置文件中使用配置元素,才真正开启了事务.
4.spring事物是基于类和接口的所以只能在类里面调用另一个类里面的事物,同一个类里面调用自己类的事物方法是无效的。spring事物也不要频繁使用,在事物处理的同时操作的第一张表会被限制查看的(即被临时锁住)。数据量大的时候会有一定影响。
springMVC的service层中一个类提供了多个方法,a()、b()、c()、d()。其中方法a上未显示声明事务(@Transactional),而方法b()、c()、d()都显示声明了事务。并且spring的注解扫描和bean的实例化过程都没有问题(排除其他问题产生的影响)。 此时controller层调用使用此service对象调用方法a(),其中处理逻辑是 a()->b()->c()->d(),有这样的调用关系。结果就是这个从方法a开始的调用没有添加上事务。 是的,乍一看有点奇怪,仔细分析一下,就清晰了。 这还得从spring AOP的实现原理说起,spring AOP是基于动态代理实现的,分jdk和cglib两种,简单来说,jdk只对接口代理,cglib可以对普通类代理。事务的实现是通过代理类(proxy)来实现的,当一个添加注解的类被动态代理后,所有调用这个类对象的方式都是先执行代理类的开启事务(如果这个方法添加了事务),再进入被代理对象执行相应的方法。但是在被代理对象中调用本对象中的方法时,不会再去通过代理类来执行(如上图1),由于事务是通过代理类完成的,所以如果父调用没有开启,后面的方法调用是不会再开启事务的。上述例子中方法a没有声明事务,所以代理类中没有对a方法开启事务,进入被代理系统后,继而调用方法b、c、d,虽然方法b、c、d上有事务注解,但因为是原代理类内部方法调用,并没有返回代理类去开启事务。故事务“失效”。 图二是理想的用法,可以在被代理类中通过AopContext.currentProxy();得到当前类的代理类,然后继续从代理类执行对应的有事务的方法。 记录一个额外的问题,有时发现事务不起作用,可能有如下原因。一般的配置方法是让Spring管理除了Controller注解以外注解,而让SpringMVC单纯管理Controller注解。也就是说Spring有一个配置文件,里面配置成扫描非Controller的bean,SpringMVC有一个配置文件,里面只扫描Controller。这样就形成了两个上下文,即Spring的上下文和SpringMVC的上下文,他们分别管理着不同的俩堆bean。这个时候你在Spring的配置文件里加了一句tx:annoation-driven,其实是告诉Spring,你管理的这些bean里面有可能会出现需要事务支持的。然后在Spring管理范围的某个bean上加了个注解@transactional,其实是用来帮助Spring识别这个bean是需要事务管理的。同样你在Spring的配置文件里加了tx:annoation-driven,SpringMVC是不关心的,他只关心你给他配置了什么以及他管理的bean上有什么注解
解决:
1、开启暴露Aop代理到ThreadLocal的支持(Spring3之后才有的)
<aop:aspectj-autoproxy expose-proxy="true"/><!—注解风格支持-->
<aop:config expose-proxy="true"><!—xml风格支持-->
这些配置信息要写到applicationcontext.xml当中。
2、修改业务类中的代码
this.b();修改为((ServiceDemo) AopContext.currentProxy()).b();
这样,当我们对b进行调用时也会首先通过当前线程的代理实现。
仍然存在的一些问题
采用上述方案可以解决业务类内部方法调用时的事务问题,但如果业务场景要求a与b中的事务是独立的,仍存在以下两个问题:
1、如果方法a与b采用的事务传播机制都是REQUIRED,那么a与b仍然采用的是同一个事务。
2、可能存在由于b的事务异常回滚而导致a也回滚,此时明显与业务要求不符。
经过测试,解决问题1的思路是,a中的事务传播机制采用REQUIRED,而b中的传播机制采用REQUIRES_NEW,就是说在调用b时,采用一个新的事务。
解决问题2的思路是,在方法a对b调用的地方加上try catch,在保证b事务正确性的前提下,不会因为b抛出的异常而影响a事务的正确性。
如果我们确定从业务上来说a、b在一个事务中是合理的,那么完全可以只在a上加事务,这样由于事务的传播特性,a、b都会使用同一个事务。
网友评论