Spring事务 #
事务实现 #
基于数据库事务和AOP
- 对于使用了@Transactional注解的Bean,创建代理对象
- 当调用代理对象的方法时,如果方法上有@Transactional注解,利用事务管理器创建一个数据库连接
- 修改数据库连接的autocommit属性为false,禁止此连接自动提交
- 执行当前方法,方法中会包含sql,没有异常就直接提交事务
- 出现异常且需要回滚则回滚事务,不需要回滚就仍然提交事务
- Spring的事务隔离级别就是数据库的隔离级别
- Spring事务的传播机制时Spring事务自己实现的,有很多种。用于设置不同场景下,对嵌套方法是否该用同一个事务执行的情况进行支持
- 传播机制是通过数据库连接来实现的,每个数据库连接一个事务,当传播机制配置为应该需要新开一个事务,实际上就是为方法新建一个数据库连接,在新连接上执行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 失效情况 #
- 由于Spring事务基于AOP代理实现,如果方法被本类地其他方法调用,则没有经过代理对象,于是事务失效
- 如果@Transactional注解的方法不是public,则失效,因为不会对非public创建代理类
- 默认情况下,Spring事务会对Error和RuntimeException进行事务回滚,对其他继承自Exception.class的异常不会回滚(如IOException等)。解决方案是写死rollbackfor = Exception.class
- 异常被catch掉,抛不到切面回滚逻辑中,事务不会回滚
- 数据库引擎不支持事务,如MySQL MyISAM没有事务,仅Innodb有
- 事务传播属性propagation 设置错误
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起,以非事务方式运行,自然不会报错回滚
- TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常,回滚当前事务