Spring事务

Spring事务 #

事务实现 #

基于数据库事务和AOP

  1. 对于使用了@Transactional注解的Bean,创建代理对象
  2. 当调用代理对象的方法时,如果方法上有@Transactional注解,利用事务管理器创建一个数据库连接
  3. 修改数据库连接的autocommit属性为false,禁止此连接自动提交
  4. 执行当前方法,方法中会包含sql,没有异常就直接提交事务
  5. 出现异常且需要回滚则回滚事务,不需要回滚就仍然提交事务
  6. Spring的事务隔离级别就是数据库的隔离级别
  7. Spring事务的传播机制时Spring事务自己实现的,有很多种。用于设置不同场景下,对嵌套方法是否该用同一个事务执行的情况进行支持
  8. 传播机制是通过数据库连接来实现的,每个数据库连接一个事务,当传播机制配置为应该需要新开一个事务,实际上就是为方法新建一个数据库连接,在新连接上执行SQL。

默认事务传播机制是:REQUIRED,如果存在事务则加入,不存在则创建

事务传播机制 #

事务传播机制指的是当事务方法被调用时,事务如何传播

传播机制的指定:

    @Transactional(propagation = Propagation.REQUIRED)
    public void calledMethod() {

    }

PROPAGATION_REQUIRED(默认) #

  • 如果调用方法不存在事务,则创建一个事务
    从数据源获取一个新的数据库连接并开启新事务
  • 如果调用方法有事务,则加入该事务
    也就是不做处理,复用当前的数据库连接和事务

效果:存在且仅存在一个事务,回滚到最开始事务创建时

  • 调用方有事务,和调用方一起作为原子操作
  • 调用方没有事务,自己是原子操作

PROPAGATION_SUPPORTS #

  • 如果调用方法不存在事务,以非事务方式运行
    获取数据源连接执行SQL,但不开启事务
  • 如果调用方法有事务,则加入该事务
    复用当前的数据库连接和事务

效果:遵从被调用方的决定,有就用,没有也不开

  • 调用方有事务,和调用方一起作为原子操作
  • 调用方没有事务,都不是原子操作

PROPAGATION_MANDATORY #

  • 如果调用方法不存在事务,抛出异常
    发现当前没有事务,抛出 IllegalTransactionStateException 异常 即要求必须在事务环境下执行
  • 如果调用方法有事务,则加入该事务
    复用当前的数据库连接和事务

效果:必须有事务才能调用该方法

  • 调用方有事务,和调用方一起作为原子操作
  • 调用方没有事务,报错

PROPAGATION_REQUIRES_NEW #

  • 如果调用方法不存在事务,则创建一个新事务
    从数据源获取一个新的数据库连接并开启新事务
  • 如果调用方法有事务,挂起当前事务,并创建新事务执行
    挂起调用者事务,获取新的链接并开启新事务,执行完毕后,回复调用者的事务

效果:不管调用方有无事务,自己都是单独事务执行 调用方和自己是两个原子,报错不影响调用方,也不被调用方影响

事务挂起的实现方式: #

Spring 调用方法时,创建新数据库链接并开启事务,用新事务执行SQL并提交或回滚,运行完毕后,再继续用原本事务

PROPAGATION_NOT_SUPPORTED #

  • 如果调用方法不存在事务,以非事务方式运行
    不从数据库连接开启事务
  • 如果调用方法有事务,挂起当前事务,以非事务方式运行
    挂起调用者事务,以非事务方式运行,执行完毕后,回复调用者的事务

效果:必须以无事务方式运行本方法

  • 调用方有事务,挂起调用方事务,用新连接不开启事务的方式执行SQL,报错不导致调用方回滚
  • 调用方没有事务,用新连接不开启事务的方式执行SQL

PROPAGATION_NEVER #

  • 如果调用方法不存在事务,以非事务方式运行
    不从数据库连接开启事务
  • 如果调用方法有事务,抛出异常
    抛出 IllegalTransactionStateException 异常,即要求不在事务环境中执行

效果:调用方法时不可以有事务,否则报错

  • 调用方有事务,报错,触发回滚当前事务
  • 调用方没有事务,用新连接不开启事务的方式执行SQL

PROPAGATION_NESTED #

  • 如果调用方法不存在事务,则创建一个新事务
    从数据源获取一个新的数据库连接并开启新事务
  • 如果调用方法有事务,在调用者事务创建嵌套事务执行任务
    在调用者的事务中创建一个嵌套事务,使用同一个数据库连接,但有独立保存点,被调用方法发生异常,只回滚到保存点,不影响调用者事务

效果:调用方法时不可以有事务,否则报错

  • 调用方有事务,创建嵌套事务执行,本方法回滚不影响调用方,调用方回滚导致本方法回滚
  • 调用方没有事务,用新连接开启事务执行SQL

嵌套事务的实现方式: #

  • MySQL事务开启后还可以用 SAVEPOINT nested_savepoint; 语句建立保存点,通过 ROLLBACK TO SAVEPOINT nested_savepoint; 方式回滚到保存点,从而实现事务内的内嵌原子操作
  • 保存点功能从MySQL5.0后 InnoDB 存储引擎开始支持

事务相关 #

@Transactional 失效情况 #

  1. 由于Spring事务基于AOP代理实现,如果方法被本类地其他方法调用,则没有经过代理对象,于是事务失效
  2. 如果@Transactional注解的方法不是public,则失效,因为不会对非public创建代理类
  3. 默认情况下,Spring事务会对Error和RuntimeException进行事务回滚,对其他继承自Exception.class的异常不会回滚(如IOException等)。解决方案是写死rollbackfor = Exception.class
  4. 异常被catch掉,抛不到切面回滚逻辑中,事务不会回滚
  5. 数据库引擎不支持事务,如MySQL MyISAM没有事务,仅Innodb有
  6. 事务传播属性propagation 设置错误
    1. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起,以非事务方式运行,自然不会报错回滚
    2. TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常,回滚当前事务