1 事务的特性
- 原子性(Atomicity)
- 一致性(Consistency)
- 隔离性(Isolation)
- 持久性(Durability)
2 Spring事务的配置方式
2.1 编程式事务管理
编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。
2.2 声明式事务管理
声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要在配置文件中做相关的事务规则声明或者通过注解的方式,便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的编程方式。唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置。
3 事务的传播机制
事务的传播性一般用在事务嵌套的场景,比如一个事务方法里面调用了另外一个事务方法,那么两个方法是各自作为独立的方法提交还是内层的事务合并到外层的事务一起提交,这就是需要事务传播机制的配置来确定怎么样执行。
常用的事务传播机制如下:
-
REQUIRED
Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行 -
REQUES_NEW
该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可 -
SUPPORT
如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务 -
NOT_SUPPORT
该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码 -
NEVER
该传播机制不支持外层事务,即如果外层有事务就抛出异常 -
MANDATORY
与NEVER相反,如果外层没有事务,则抛出异常 -
NESTED
该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常捕获掉,基本还是会引起全部回滚的。
以上的源码信息请参考Spring的枚举类:org.springframework.transaction.annotation.Propagation
,该枚举类可以赋值给@Transactional注解的propagation属性以实现事务的传播性。
4 隔离级别
隔离级别请参考MySQL-InnoDB引擎的事务--隔离级别
在编程中指定隔离级别:给@Transactional注解中的isolation属性赋值org.springframework.transaction.annotation.Isolation
枚举变量即可实现不同的隔离级别。当不赋值或者赋值为默认时,Spring将使用jdbc对应的默认隔离级别。
Spring默认隔离级别枚举变量源码注释:
Use the default isolation level of the underlying datastore. All other levels correspond to the JDBC isolation levels.
4 只读
如果一个事务只对数据库执行读操作,那么该数据库就可能利用那个事务的只读特性,采取某些优化措施。通过把一个事务声明为只读,可以给后端数据库一个机会来应用那些它认为合适的优化措施。由于只读的优化措施是在一个事务启动时由后端数据库实施的, 因此,只有对于那些具有可能启动一个新事务的传播行为(REQUIRES_NEW、REQUIRED、NESTED)的方法来说,将事务声明为只读才有意义。
Spring源码注释:
This just serves as a hint for the actual transaction subsystem; it will not necessarily cause failure of write access attempts. A transaction manager which cannot interpret the read-only hint will not throw an exception when asked for a read-only transaction but rather silently ignore the hint.
通过将@Transactional注解中readOnly属性赋值为true来开启只读,默认为false。
5 事务超时
为了使一个应用程序很好地执行,它的事务不能运行太长时间。因此,声明式事务的下一个特性就是它的超时。
假设事务的运行时间变得格外的长,由于事务可能涉及对数据库的锁定,所以长时间运行的事务会不必要地占用数据库资源。这时就可以声明一个事务在特定秒数后自动回滚,不必等它自己结束。
由于超时时钟在一个事务启动的时候开始的,因此,只有对于那些具有可能启动一个新事务的传播行为(REQUIRES_NEW、REQUIRED、NESTED)的方法来说,声明事务超时才有意义。
通过将@Transactional注解中timeout属性赋值为一个int值(单位秒?)来指定事务的超时时间,默认为-1即无超时。
6 回滚规则
在默认设置下,事务只在出现运行时异常(runtime exception)时回滚,而在出现受检查异常(checked exception)时不回滚(这一行为和EJB中的回滚行为是一致的)。
不过,可以声明在出现特定受检查异常时像运行时异常一样回滚。同样,也可以声明一个事务在出现特定的异常时不回滚,即使特定的异常是运行时异常。
通过将@Transactional注解中rollbackFor属性(数组)赋值为一个或多个异常类的Class来让该事务在抛出这些异常或及其子异常时回滚,默认为空数组。
通过将@Transactional注解中noRollbackFor属性(数组)赋值为一个或多个异常类的Class来让该事务在抛出这些异常或及其子异常时不回滚,默认为空数组。
当然也可以给属性rollbackForClassName和noRollbackForClassName赋值异常类的全类名来达到同样的效果。
7 示例
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, timeout = 5, readOnly = true)
public void doSomething() {
// ...
}
设置事务传播为REQUIRES_NEW,设置隔离级别为SERIALIZABLE,设置回滚策略为当抛出Exception异常时进行回滚,设置超时时间为5秒,设置只读。