Spring事务梳理

Spring事务传播行为

概述
  • Spring在TransactionDefinition接口中规定了7种类型的事务传播行为
  • 事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。
事务的分类
  • Java事务类型分为JDBC事务跟JTA事务
    • JDBC事务:即为上面说的数据库事务中的本地事务,通过connection对象控制管理。
    • JTA事务:JTA指Java事务API(Java Transaction API),是Java EE数据库事务规范, JTA只提供了事务管理接口,由应用程序服务器厂商(如WebSphere Application Server)提供实现,JTA事务比JDBC更强大,支持分布式事务。
  • 按是否通过编程
    • 声明式事务:通过XML配置或者注解实现。
    • 编程式事务:通过编程代码在业务逻辑时需要时自行实现,粒度更小。
数据库特性
  • 事务的数据库,都必须具备四个特性,分别是:
    • 原子性(Atomicity)
    • 一致性(Consistency)
    • 隔离性(Isolation)
    • 持久性(Durability)
隔离级别
  • 数据库隔离级别
    • Read-Uncommitted (未提交读)
    • Read-Committed (提交读)
    • Repeatable-Read(可重复读)
    • Serializable (可串行化)
  • Spring中的隔离级别 (除了第一种,剩余四个可以对应数据库的隔离级别)
    • ISOLATION_DEFAULT 这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与 JDBC 的隔离级别相对应。
    • ISOLATION_READ_UNCOMMITTED
    • ISOLATION_READ_COMMITTED
    • ISOLATION_REPEATABLE_READ
    • ISOLATION_SERIALIZABLE
Spring中七种事务传播行为
  • PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
  • PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
  • PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与- PROPAGATION_REQUIRED类似的操作。
spring的事务超时
Java 中定义了两类异常
    1. Checked exception: 这类异常都是Exception的子类。异常的向上抛出机制进行处理,假如子类可能产生A异常,那么在父类中也必须throws A异常。可能导致的问题:代码效率低,耦合度过高。
    1. Unchecked exception: 这类异常都是RuntimeException的子类,虽然RuntimeException同样也是Exception的子类,但是它们是非凡的,它们不能通过client code来试图解决,所以称为Unchecked exception 。
事务回滚规则
  • Spring的事务管理默认是针对unchecked exception回滚,也就是默认对Error异常和RuntimeException异常以及其子类进行事务回滚,且必须抛出异常
  • 若使用try-catch对其异常捕获则不会进行回滚!(Error异常和RuntimeException异常抛出时不需要方法调用throws或try-catch语句);而checked exception 则必须用try语句块进行处理或者把异常交给上级方法处理总之就是必须写代码处理它,所以必须在service捕获异常,然后再次抛出,这样事务方才起效。
使用
  • @Transactional(propagation=Propagation.REQUIRED)
    • propagation 属性 事务的传播行为,默认值为 Propagation.REQUIRED。
    • isolation 属性 事务的隔离级别,默认值为 Isolation.DEFAULT。
    • timeout 属性 事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
    • readOnly 属性 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
    • rollbackFor 属性 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。
    • noRollbackFor 属性 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。
事务不回滚的原因
  • 声明式事务配置切入点表达式写错了,没切中Service中的方法
  • Service方法中,把异常给try catch了,但catch里面只是打印了异常信息,没有手动抛出RuntimeException异常
  • Service方法中,抛出的异常不属于运行时异常(如IO异常),因为Spring默认情况下是捕获到运行时异常就回滚
@Transactional事务几点注意
  • 不要在接口上声明@Transactional ,而要在具体类的方法上使用 @Transactional 注解,否则注解可能无效。
  • 不要图省事,将@Transactional放置在类级的声明中,放在类声明,会使得所有方法都有事务。故@Transactional应该放在方法级别,不需要使用事务的方法,就不要放置事务,比如查询方法。否则对性能是有影响的。
  • 使用了@Transactional的方法,对同一个类里面的方法调用, @Transactional无效。比如有一个类Test,它的一个方法A,A再调用Test本类的方法B(不管B是否public还是private),但A没有声明注解事务,而B有。则外部调用A之后,B的事务是不会起作用的。(经常在这里出错)
  • 使用了@Transactional的方法,只能是public,@Transactional注解的方法都是被外部其他类调用才有效,故只能是public。道理和上面的有关联。故在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错,但事务无效。
  • spring的事务在抛异常的时候会回滚,如果是catch捕获了,事务无效。可以在catch里面加上throw new RuntimeException();
  • 经过在ICORE-CLAIM中测试,效果如下

    A.抛出受查异常XXXException,事务会回滚。
    B.抛出运行时异常NullPointerException,事务会回滚。
    C.Quartz中,execute直接调用加了@Transactional方法,可以回滚;间接调用,不会回滚。(即上文3点提到的)
    D.异步任务中,execute直接调用加了@Transactional方法,可以回滚;间接调用,不会回滚。(即上文3点提到的)
    E.在action中加上@Transactional,不会回滚。切记不要在action中加上事务。
    F.在service中加上@Transactional,如果是action直接调该方法,会回滚,如果是间接调,不会回滚。(即上文3提到的)
    G.在service中的private加上@Transactional,事务不会回滚。