1,概念
提交(commit):
将未存储的SQL语句写入数据库表;
保留点(savepoint):
事务处理中设置的临时占位符(placeholder),可以对它
发布回退(与回退整个事务处理不同)。
保留点越多越好,越多回退会更灵活。
2,事务抽象
Spring Framework对事务管理做了一层抽象:TransactionManager,它是一个空接口。
PlatformTransactionManager 接口继承了TransactionManager,是事务管理的核心接口,包含了事务获取、提交、回滚的三个方法。
不同数据访问技术对应的PlatformTransactionManager实现:
TransactionManager类 | 数据访问技术 | 举例 |
---|---|---|
DataSourceTransactionManager | 在仅使用JDBC时使用 | Spring Boot 默认使用JDBC来控制事务 |
JpaTransactionManager | 在使用JPA时使用。同时在实现时还可能使用JDBC | |
HibernateTransactionManager | 在使用Hibernate时使用。同时在实现时还可能使用JDBC | |
JtaHibernate | 在使用全局事务(也就是使用应用程序服务器的分布式事务管理功能)时使用。此时可以使用任何数据访问技术 |
3,事务属性
TransactionDefinition接口描述了事务定义,包含了几个与事务密切相关的属性:
1)传播性
事务传播性定义了7个级别,规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:
A方法调用B方法,B定义了以下传播行为:
传播行为类型 | 值 | 规则 | 举例 |
---|---|---|---|
PROPAGATION_REQUIRED 请求事务(required) | 0(默认) | 当前有事务就用当前事务,没有事务就重启一个事务。 | A无事务,B开启一个新事务; A有事务,那么B加入A的事务。B异常,一起回滚。 |
PROPAGATION_SUPPORTS 可支持事务 | 1 | 事务不是必须的,可以有也可以没有 | A无事务,B也无; A有事务,那么B加入A的事务。 |
PROPAGATION_MANDATORY 强制事务(mandatory,强制的) | 2 | 必须有一个事务,否则报错 | A无事务,就抛出异常;===>强制必须有事务 A有事务,那么B加入A的事务。 |
PROPAGATION_REQUIRES_NEW 请求新事务 | 3 | 新启动一个事务,如果当前存在一个事务则将其挂起 | A无事务,B新建事务; A有事务,B新建一个事务,且与A的事务独立。B异常,B回滚;A如果吃掉了B的异常,不回滚。 |
PROPAGATION_NOT_SUPPORTED 不支持事务 | 4 | 不支持事务,以非事务方式运行 | A无事务,B也无; A有事务,B把A的事务挂起,无事务执行 |
PROPAGATION_NEVER 拒绝事务 | 5 | 不支持事务,如果当前存在一个事务则抛异常 | - |
PROPAGATION_NESTED 嵌套事务(nested,嵌套) | 6 | 如果当前存在一个事务,则在该事务内再启动一个事务 | A无事务,B创建新事务; A有事务,B创建其子事务。==》父事务回滚,子也回滚。子事务异常,回滚;A如果吃掉了B的异常,不回滚。 B方法被调用时创建了一个保存点,B异常A不一定回滚。 |
2)隔离级别
对应数据库的四个隔离级别定义了4种隔离类型,默认隔离级别设置为-1,使用底层数据源的配置。
比如:mysql默认REPEATABLE_READ;Oracle和pg默认READ_COMMITTED。
3)超时时间
4)是否只读
4,注解方式
1)@Transactional 使用
- 开启注解式事务的支持
@EnableTransactionManagement
; @Transactional(rollbackFor = Exception.class, propagation = Propagation.PROPAGATION_REQUIRED)
常用属性有:
属性 | 默认值 | 作用 |
---|---|---|
transactionManager 或者value | transactionManager | 指定事务管理器 |
propagation | REQUIRED | 事务传播性 |
isolation | DEFAULT | 事务隔离级别 (使用的同时需要底层数据库支持这些值) |
timeout | TIMEOUT_DEFAULT,-1 | 事务超时时间; 超时自动回滚事务。 |
readOnly | false | 是否为只读事务;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。 |
rollbackFor rollbackForClassName | java.lang.RuntimeException或者子类 | 异常时回滚,可用于单元测试; |
noRollbackFor | java.lang.Exception或者子类 | 当期待的异常发生时,不回滚 |
@Transactional
作用域
作用域 | 说明 |
---|---|
类 | 表示类中的所有公共方法都被事务化 |
方法 | 优先级高于类的作用域 |
- 提前回滚
又是没有异常发生,我们依然想回滚:
(可以用在单元测试上测试回滚)
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
2)@Transactional 原理
目标方法声明式调用 @Transactional,通过AOP代理,目标方法形成一个切面对象,在spring的bean的初始化过程中,创建代理对象注册到IOC容器中。方法执行前先把事务自动提交设置为false;方法执行时如果没有异常,会自动将事务提交。默认会对方法中的RuntimeException和Error进行回滚。
3)@Transacational 失效场景
- 数据库不支持事务;
- 异常失效导致事务不回滚;
异常被吃掉 或者 checked 异常,此时不回滚事务。 - aop场景失效
a) 方法自调用;==》相当于this.方法调用,不走aop。
解决方式:通过aop方式调用;如果不能通过@Autowired
的对象调用,也可以从ApplicationContext中获取自己的代理对象操作这个方法。
b) 类没有被注入IOC容器; - 非public方法
proxy-target-class=true
,那么基于类的代理(CGLIB动态代理)将起作用;
缺省为false
,那么标准的JDK基于接口的代理将起作用。
a) JDK:只能动态代理public
或public final
修饰符的方法;
b) CGLIB:由于使用final,static,private修饰符的方法都不能被子类复写,即:除了public
的非final的实例方法,其他方法均无效;
c) ApectJ代理模式:非public修饰的方法也可以被代理。 - 多个事务管理器。
多数据源情况下,存在多个事务管理器时,如果不指定事务管理器,会按照事务管理器在配置文件中的初始化顺序使用其中一个。显示指定采用哪个事务@Transaction(value=“xxx”)
5,多数据源事务管理
1)单数据源事务
直接用@Transactional 注解,无需自己管理事务,Spring Boot 默认使用DataSourceTransactionManager 来管理事务。
@Transactional注解的value属性就是用来指定具体TransactionManager的,通过id或者name来指定唯一一个TransactionManager。
@Transactional(value = "database2TransactionManager")
public void test(String a) {
// business operation
}
2)分布式事务管理器(JTA)
在使用Transactional注解的时候,通过value指定不同的事务管理器。
1>场景
- 一个方法里操作两个数据源;
- 一个数据分布在不同的节点上。
2>实现
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.data.neo4j.transaction.Neo4jTransactionManager;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManagerFactory;
@Configuration
public class TransactionalConfig {
/**
* pg事务管理器 (默认)
*/
@Bean
@Primary
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
/**
* 定义neo4j事务管理器
*/
@Bean("neo4jTransactionManager")
public Neo4jTransactionManager neo4jTransactionManager(SessionFactory sessionFactory) {
return new Neo4jTransactionManager(sessionFactory);
}
/**
* 多事务管理器
* ChainedTransactionManager只是同时开启两个事务,自动的对两个事务,同时开启,同时提交(或回滚)。它对多个数据写数据,并不是一个原子操作,不能像XA那样保证对数据操作的完整性,一致性。
看源代码:它用一个列表来管理多个数据源的事务管理器。
*/
@Autowired
@Bean(name = "multiTransactionManager")
public PlatformTransactionManager multiTransactionManager(
Neo4jTransactionManager neo4jTransactionManager,
JpaTransactionManager pgTransactionManager) {
return new ChainedTransactionManager(
neo4jTransactionManager, pgTransactionManager);
}
}
有个坑:如果在junit中测试(加上@Transactional):无论如何都不能成功提交,会rollback。
3>XA规范
XA是分布式事务规范,定义了分布式事务模型。定义了以下四个角色:
- 应用程序(AP)
- 事务管理器(协调者TM)
- 资源管理器(参与者RM)
- 通信资源管理器(CRM)
JTA是java对XA规范的实现。
AP告诉TM要开始事务,TM协调RM并将事务的xid分配给每个RM。
a)两阶段协议
- prepare阶段
每个参与者执行本地事务,但不提交,进入ready状态,并通知TM已经准备就绪。 - commit阶段
当TM收到每个参与者都ready后,通知所有参与者commit;如果有参与者是fail,则发送rollback命令,所有参与者做回滚操作。
这个阶段如果发生了网络故障容易引发数据不一致问题。
b)三阶段协议
优化两阶段单点故障问题,但是不一致问题依然无法解决。
超时机制:如果preCommit消息执行成功,一定时间后还没有收到doCommit消息,则会认为TM挂掉了,自己执行doCommit。
- 发送canCommit消息,确认数据库环境正常(其他结点都正常);
- 发送preCommit消息,完成sql操作;
- 发送doCommit消息,通知所有库提交事务/回滚。
c)TCC(补偿事务)
针对每个操作,都要注册一个与其对应的确认、撤销操作。
- Try
检查资源;try之后执行具体业务逻辑。 - Confirm
确认执行完成 可以是空方法。执行完后整个业务事务完成。 - Cancel
执行撤销操作。执行完后整个业务事务完成。
4>消息队列的事务消息
A调用B时,通过MQ来调用,将调用信息放MQ中。
- A执行事务,将message放入MQ1中。
- A执行本地事务。
- A执行完毕,发送一条commit消息给MQ1,MQ1数据出队并入MQ2中。B监控并消费MQ2数据,如果失败不断重试消费。
如果A执行失败,发送消息给MQ1,删除所有数据。