Spring事务注解@Transactional的坑爹陷阱

来源:互联网 发布:淘宝宝贝详情页教程 编辑:程序博客网 时间:2024/05/29 15:38

生产系统核心部分特别是涉及到账户的批量处理,放在凌晨定时运行,结果出问题了,现象很诡异:

1)问题偶发,但没有规律

2)程序没有按照预定步骤处理数据库相关数据

3)发生后也没有发现有死锁,但出现异常的锁

前后折腾了三次,但一直没有找到根源。


后来另外一个问题发生时,分析发现数据库事务处理形同虚设,用Spring @Transactional注解的数据库事务完全没有生效,让人费解,甚至怀疑所用的PostgreSQL数据库是不是有问题,在事务控制方面是不是有Bug。


后来查询资料,发现是对Spring @Transactional事务注解理解不当,使用有误造成的。坑爹的陷阱,让团队加了好几次班 敲打


由于Spring是通过AOP方式实现@Transactional事务注解的,由于AOP的局限性,在一些特定的情况下,@Transactional注解不会生效,直接导致数据库的并发控制出现问题。


以下是一篇关于Spring @Transactional注解失效的一篇文章,文章还提供了一个工具类,用来调试判断当前事务是否开启。

http://blog.timmattison.com/archives/2012/04/19/tips-for-debugging-springs-transactional-annotation/

文章的要点如下:

  1. 1)  @Transactional annotations only work on public methods. If you have a private or protected method with this annotation there’s no (easy) way for Spring AOP to see the annotation. It doesn’t go crazy trying to find them so make sure all of your annotated methods are public.

  2. 2)Transaction boundaries are only created when properly annotated (see above) methods are called through a Spring proxy. This means that you need to call your annotated method directly through an @Autowired bean or the transaction will never start. If you call a method on an @Autowired bean that isn’t annotated which itself calls a public method that is annotatedYOUR ANNOTATION IS IGNORED. This is because Spring AOP is only checking annotations when it first enters the @Autowired code.

  3. 3)Never blindly trust that your @Transactional annotations are actually creating transaction boundaries. When in doubt test whether a transaction really is active (see below)

另有一篇中文文章也阐述了类似问题和观点
http://blog.itpub.net/273449/viewspace-1057345/
@Transactional spring 配置事务 注意事项 

1. 在需要事务管理的地方加@Transactional 注解。@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上

2. @Transactional 注解只能应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。

3. 注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据。必须在配置文件中使用配置元素,才真正开启了事务行为。

4. 通过 元素的 "proxy-target-class" 属性值来控制是基于接口的还是基于类的代理被创建。如果 "proxy-target-class" 属值被设置为 "true",那么基于类的代理将起作用(这时需要CGLIB库cglib.jar在CLASSPATH中)。如果 "proxy-target-class" 属值被设置为 "false" 或者这个属性被省略,那么标准的JDK基于接口的代理将起作用。





0 0
原创粉丝点击