转:
Spring Boot 事务传播机制级别/类型/行为、嵌套事务、事务隔离机制
首先呢? 事务的传播行为是针对 ‘嵌套事务而言’ 疑问所在?
先说 Spring 传播机制级别/类型、
~七种:事务传播机制/行为 ~ ~五类:事务隔离级别
- Required(adj. 必需的)|默认 -Default(v. 不履行)默认
- Requires_new(vt. 需要New) -Read_Uncommitted(读未提交)
- Mandatory(adj. 强制的) -Read_Committed(读已提交)
- Supports(v. 支持) -Repeatable_Read(可重复读)
- Ont_Supported(adj. 不支持的) -Serializable(串行化)
- Never(adv. 从未)
- Nested(adj. 嵌套的,内装的)
==源码段
@Transactional()
{
/*默认-事务传播机制/行为 */
Propagation propagation() default Propagation.REQUIRED;
/** 默认 Required 行为
* ·该传播机制指:<标注事务注解的方法体:·正在使用一个事务,则同加入当前事务中;
* ·如果没有,自己创建一个新事务,方法必须运行在事务中
* ·(存在事务就加入事务/或创建事务 成为一个整体,失败回滚也同一起儿Rollback-Only-)
* · 整体一起提交、一起回滚
*
* Requires_new(vt. 需要New)
* ·每次创建一个新事务,如果当前已经在事务中了,会挂起(Suspend)当前事务.
* · 场景是: 该方法是个事务方法,方法内调用了另外一个事务方法==简称==外内(内外)双事务/嵌套多事务
* · 被嵌套的事务称:内层_事务、 调用其它事务的方法体称:外层_事务
* · -内层事务执行结束后、提交,与外事务无关联.
* · -内层事务异常回滚,外层事务如没有try-catch处理异常,两者一同回滚,如处理过,外层不会回滚.
* · -如没有两个以上的事务,默认被动使用默认 Required.
* · -内层事务的传播机制为:Requires_new(vt. 需要New)
*
* Nested(adj. 嵌套的,内装的)
* · 如果当前有事务,则嵌套在已存在的事务中作为一个其子事务(嵌套事务)
* · 嵌套事务是独立回滚的,外层不受影响
* · 但如果主事务提交,则会携带子事务一起提交,一起回滚
* · 子事务异常,则父事务可以回滚或不回滚
* · -内层事务的传播机制为:Nested(adj. 嵌套的,内装的)
*
* Mandatory(adj. 强制的)
* · 该事务是强制性必须存在一个事务/独占,如果没有,则抛出异常.
* · 调用必须用事务,外层没有事务抛出异常
* · 同外事务一起提交、一起回滚.
*
* Ont_Supported(adj. 不支持的)
* · 事务存在就挂起,方法体执行完毕后,事务恢复进行(外层事务)
* · 方法体与事务无关,执行完就会提交,不受外层事务是否结束后再提交
* · 内层事务:Ont_Supported(adj. 被/不支持的)
*
* Supports(v. 支持)
* · 当前有事务就使用事务,当前没有则无状态/不使用事务.
* · 单个方法调用时Supports事务传播机制行为,同 Never行为状态 一样.
* · 外层事务有事务,它就有事务,其它无状态
*
* Never(adv. 从未)
* · 当前有事务存在,则抛出异常
* · 不支持事务
* · 不能被事务操作,但有事务方法调用就抛出异常
*/
/*默认-事务隔离级别 */
Isolation isolation() default Isolation.DEFAULT;
/** 默认 Default(v. 不履行)
* · 标识使用数据库默认的事务隔离级别
* · MySql 默认隔离级别:-Repeatable_Read(可重复读)
* · Oracle 默认隔离级别:-Read_Committed(读已提交)
~剩下 四个事务隔离级别与JDBC的隔离级别相对应~.
* -Read_Uncommitted(读未提交) 脏读、不可重复读、幻读问题
* ·允许其它事务看到,当前事务未提交的数据.
*
* -Read_Committed(读已提交) 不可重复读、幻读问题
* · 保证一个事务修改数据已提交后,才能被另外一个事务读取.
*
* 不可重复读 就是一个事务读到另一个事务修改后并提交的数据(update)。
* 可重复读 就是不允许其它事务操作/独占策略 (行锁机制:不能更新/删除数据)
* -Repeatable_Read(可重复读) 幻读问题
* -Serializable(串行化) (表锁机制)
*/
/*默认-事务超时 */
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
/** 默认None(没有)超时时间[-1]、事务超时自动回滚-Rollback
* 疑问开始之前,先说四个不同层de超时相关的设置:
* 1-Spring的@Transaction的TimeOut(超时)
* · 目的是为了一个事务执行时间超过某个预知时间后抛出异常.
* · 一个事务中包含多个Statement(声明)对象
* · 但一个Statement(声明)的执行时间流程:
* 包括(Mybatis的TimeOut、Jdbc的TimeOut、Mysql中的innodb_Lock_Wait_TimeOut(超时))
* ·以及建立Connection连接的持续时间,及连接Mysql的Wait_TimeOut(超时问题).
* 2-1-Mybatis的TimeOut(超时)
* · 目的是为了在某个Statement(声明)时间太久时被中止.
* 2-2-JDBC——Connection连接池的Wait_TimeOut(间隔超时)
* · 目的持续Connection 连接执行最近一个Statement(声明)
* 到当前Statement(声明)的时间间隔
* ·JDBC的ConetionTimeout和SocketTimeout不设置,MysqlServer端突然中断,客户端一直阻SocketInputStream.Read()上;
* ·DruidDataSource的ConnectionTimeout和SocketTimeout两个属性来实现.
* ·jdbc:mysql://xxx.xx.xxx.xxx:3306/database?connectTimeout=60000&socketTimeout=60000.
* 4-Mysql的innodb_Lock_Wait_TimeOut(超时)
* · 目的是针对两个事务,同时更新同一行记录时,等待的最长时间.
* · 该变量...Wait_TimeOut..,事务等待行锁的时间。默认是50s.
* · Mysql指令查看超时变量: show variables like '%timeout%'
* · innodb_Rollback_On_TimeOut变量,事务等待超时之后,进行回滚的策略。默认是OFF(关闭),
* · 表示只回滚到上一个Statement,每执行一个SQL语句(一个Satement)就有一个保存点,
* · 此时是回滚到上一个保存点。如果这个值改为ON(打开),那此时事务就进行整体回滚,即执行了Rollback。
* Mysql的Wait_TimeOut(超时问题).
* · 通过 show processlit 指令可以 查看连接Connection线程Thread连接Sleep(睡眠)时间数值
* · 这个时间数值是这个Connection执行最近一个Statement到当前的时间间隔。
* 这个时间就是设置开一个客户端,不做任何操作的时候的连接保持时间。
*
* 总结一下:
* (1) Spring_@Transaction_TimeOut、Mybatis_TimeOut、JDBC级别的TimeOut(超时)并没有更改Mysql的属性超时,
* # 归纳一下前三者的实现:
* 一· 对于Spring的@Transactional是通过TimeOut属性初始化了一个Deadline(最终期限),
* 每一次创建Statment判断Deadline是否小于0,如果小于0抛异常;
* 否则通过JDBC的Statement#setQueryTimeout()来设置超时.
* 二· Mybatis的TimeOut也是通过通过JDBC的Statement#setQueryTimeOut来设置超时。
* 三· JDBC的TimeOut是在Statement执行时,开启了一个监听线程发现超时就终端当前执行的Statement,然后抛异常。
* # 最重要一点儿,只有MySQL层没有超时的情况下。上层的JDBC或者Spring层的TimeOut才有意义。
*
*
*
* 说下Mysql中的核心处理一节:
* Statement接口对象(java.sql.Statement)的QueryTimeout(查询超时)处理过程;
* · 通过调用Connection接口的CreateStatement()方法创建Statement对象接口(该对象用于执行静态SQL语句的对象),
* · 调用Statement的ExecuteQuery()方法,返回ResultSet对象接口(该对象代表数据库结果集的数据表),
* · Statement接口对象的setQueryTimeOut()方法,设置超时时间,
* · Statement通过自身Connection将Query发送给MySQL数据库,
* · Statement创建一个新的TimeOut-Execution线程用于超时处理,
* · 每次、每个Connection接口对象都会分配一个TimeOut-Execution线程,
* · 然后向TimeOut-Execution线程进行注册,达到超时时间
* · TimerThread调用JTDS-Statement实例中的TsdCore.cancel()取消方法
* · Timeout-Execution线程创建一个和Statement配置相同的Connection,
* · 使用新创建的Connection向超时Query发送Cancel取消 Query(Kill Query “ConnectionId”)
*
*/
}
转: