Spring对事务(Database Transaction)的管理

最近的一个项目在写JavaWeb,在service层需要用到@Transaction,之前也用过但为什么用、怎么用、原理了解并不多,查查资料做个总结记录。

参考资料:
  • 1:英文Spring Doc
  • 2:中文翻译
  • 3:Spring事务管理详解
  • 4:Spring源码解析

其中资料4是针对Spring2.0的,我看了看Spring3.2.14的源码和2.0的大同小异,理解编程思路即可。建议先看Doc,英文不懂的看翻译。然后再看看资料3,最后看源码。下面记录一下笔记:

1、什么是事务

事务作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。

  • 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
  • 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
  • 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
  • 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

2、为什么要用Spring的事务管理

这部分也就是Doc中提到的Spring的优势,Spring解决了原来J2EE开发需要面临的全局事务本地事务,Spring解决以上两者的不足。它使应用开发者能够使用在任何环境下使用一致的编程模型。Spring框架同时提供声明式和编程式事务管理。声明事务管理是多数使用者的首选。

3、声明式事务管理的用法

编程式事务管理主要采用分别是:TransactionTemplate和直接使用PlatformTransactionManager。编程式对自身代码侵入性较强,不符合我大Spring的高雅风(bi)格。具体用法详见资料。
声明式有两种写法:

  • xml配置
  • @Transactional
    xml好处是非常清晰。@Transactional好处是使用简单,但需要Java1.5的支持,因为注解基于动态代理和反射,这是Java1.5出的特性。这里重点介绍注解式声明对事务管理。记得在applicationContext.xml中配置如下,为什么请见Doc:
    

    
        
    

通过@Transactional注解可以设置以下事务属性:

Spring对事务(Database Transaction)的管理_第1张图片
注解添加属性.png

关于每种属性的意义,属性中参数的不同所代表的事务区别这里不再赘述。

推荐文章开头的资料三。

4、划重点:

  • 当项目中使用几个相关的业务操作,建议放在同一个事务中,看具体情况可以采用嵌套事务是否在某个操作失败后全部回滚还是部分回滚。

  • 嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:它们非常类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。使用 PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。

  • 隔离级别用来避免并发事务引起的:脏读(读了未commit数据)、幻读(注重增删)、不可再重复读(注重改)

  • 理解Spring的声明式事务管理方面最重要的概念是:Spring的事务管理是通过AOP代理实现的。 其中的事务通知由元数据(目前基于XML或注解)驱动。 代理对象与事务元数据结合产生了一个AOP代理,它使用一个PlatformTransactionManager 实现品配合TransactionInterceptor,在方法调用前后实施事务。关于AOP推荐Spring AOP 中文Doc

  • Spring团队的建议是你只在具体的类上使用 @Transactional 注解, 而不要注解在接口上。你当然可以在接口(或接口方法)上使用 @Transactional 注解, 但是这只有在你使用基于接口的代理时它才会生效。因为注解是 不能继承 的, 这就意味着如果你正在使用基于类的代理时,事务的设置将不能被基于类的代理所识别,而且对象也不会被事务代理所包装 (这是很糟糕的)。 因此,请接受Spring团队的建议,在具体的类(包括该类的方法)上使用 @Transactional 注解。

  • 在使用代理的时候,@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,系统也不会报错, 但是这个被注解的方法将不会执行已配置的事务设置。如果你非要注解非公共方法的话,请参考使用AspectJ。

  • 在代理模式下(默认的情况),只有从代理传过来的‘外部’方法调用才会被拦截。 这就意味着‘自我调用’是不会触发事务的,比如说,一个在目标对象中调用目标对象其他方法的方法是不会触发一个事务的,即使这个方法被标记为 @Transactional。如果你期望‘自我调用’被事务覆盖到,可以考虑使用AspectJ 模式。

欢迎在评论区批评建议,另外周末写一篇介绍《Android新手项目:扫描和生成二维码的app构建》文章,从头到尾介绍一个Android App构建过程。

你可能感兴趣的:(Spring对事务(Database Transaction)的管理)