事务
- 事务常用类
- TransactionSynchronizationManager
- afterCommit
- 事务配置
- Aop配置
- 切点的配置
- @Transaction配置
- 事务传播属性
- PROPAGATION_REQUIRED
- PROPAGATION_SUPPORTS
- PROPAGATION_MANDATORY
- PROPAGATION_REQUIRES_NEW
- PROPAGATION_NOT_SUPPORTED
- PROPAGATION_NEVER
- PROPAGATION_NESTED
- 场景说明
- 事务传播属性与connection关系
- propagation="NEVER”
- 无事务配制无传播属性
- 事务隔离级别
- 隔离级别种类
- ISOLATION_DEFAULT
- ISOLATION_READ_UNCOMMITTED
- ISOLATION_READ_COMMITTED
- ISOLATION_REPEATABLE_READ
- ISOLATION_SERIALIZABLE
- 隔离级别的场景
- 隔离级别的原理
事务常用类
TransactionSynchronizationManager
afterCommit
事务配置
Aop配置
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 以方法为单位,指定方法应用什么事务属性 isolation:隔离级别 propagation:传播行为-->
<tx:method name="doTx*" isolation="READ_COMMITED" propagation="REQUIRED" />
<tx:method name="doInvoke*" isolation="READ_COMMITED" propagation="REQUIRED" />
<tx:method name="doNewTx*" isolation="READ_COMMITED" propagation="REQUIRES_NEW" />
<tx:method name="doNoTx*" isolation="READ_COMMITED" propagation="NOT_SUPPORTED" />
</tx:attributes>
</tx:advice>
<!-- 配置织入 -->
<aop:config>
<!-- 配置切点表达式 -->
<aop:pointcut expression="execution(* com.mskj.service.*ServiceImpl.*(..))" id="txPointcut" />
<!-- 配置切面 : 通知+切点 advice-ref:通知的名称 pointcut-ref:切点的名称 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref=" txPointcut " />
</aop:config>
切点的配置
(1)第一个*表示方法的返回值是任意的
(2) service.*ServiceImpl表示service包下以ServiceImpl结尾的class
(3) service.*ServiceImpl.*表示service包下以ServiceImpl结尾的class的所有方法
(4)(…)表示方法参数任意
@Transaction配置
如果要使用@Transaction注解则必须配置
<tx:annotation-driven transaction-manager="transactionManager" mode="proxy" proxy-target-class="false"/>
事务传播属性
PROPAGATION_REQUIRED
如果存在一个事务,则支持当前事务。如果没有事务则开启新的事务。
PROPAGATION_SUPPORTS
如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
PROPAGATION_MANDATORY
如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
PROPAGATION_REQUIRES_NEW
总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
PROPAGATION_NOT_SUPPORTED
总是非事务地执行,并挂起任何存在的事务
PROPAGATION_NEVER
总是非事务地执行,如果存在一个活动事务,则抛出异常
PROPAGATION_NESTED
如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
场景说明
交易正向处理流程:(业务库中的处理无跨库事务)
1 事务开启(REQUIRED)
2 记录技术流水,使用独立事务(REQUIRES_NEW) 这步要是出现异常 后续操作不会执行
3 在业务库读取业务数据(REQUIRED)(会在第5步结束之后事物挂起 最后提交见下方说明)
4 在业务库新增业务数据(REQUIRED)
5 在业务库修改业务数据(REQUIRED)
6 读取公共库的数据,不使用事务(NOT_SUPPORTED)
7 修改技术流水状态,使用独立事务(REQUIRES_NEW)
以下场景均经过测试验证:
1) 均无异常
第2步获取数据库连接1,并直接提交事务;
第3步获取新的数据库连接2,并在第5步之后挂起;
第6步获取新的数据库连接3,并读取数据成功;
第7步获取新的数据库连接4,并独立提交事务;
第3步获取的数据库连接2所对应的事务,最后提交。
2) 第2步记录技术流水异常,独立事务回滚,后续操作均不执行。
注意:模拟测试与实际场景不同,实际情况下,技术流水的记录由拦截栈来处理,流水记录失败是否进行后续业务操作,请以平台代码为准。
3) 第5步修改业务数据异常,第2步的流水记录新增正常提交,第4步的insert操作回滚,后续操作不执行。
4) 第6步读取公共库数据异常,第2步的流水记录新增正常提交,第4步的insert操作、第5步的update操作回滚,后续操作不执行。
注意:最外层的事务回滚,是因为第6步的异常抛到了外层,如果第6步内部处理掉异常,则不会导致最外层事务回滚。
事务传播属性与connection关系
propagation="NEVER”
当传播属性为never时 执行操作时,connection还是与线程变量绑定,一直都是同一个connection在操作 测试结果如下:
edit connection hashcode=181260145
updateById connection hashcode=181260145
无事务配制无传播属性
每一次的更新 都分别从DataSourceUtils里面重新获取一个新连接 就没有把连接绑定到线程变量这一说了
事务隔离级别
spring提供了5种隔离级别
隔离级别种类
ISOLATION_DEFAULT
这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别
ISOLATION_READ_UNCOMMITTED
这是事务最低的隔离级别,它允许令外一个事务可以看到这个事务未提交的数据 会出现脏读 用的较少
ISOLATION_READ_COMMITTED
比较常用 :保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据
ISOLATION_REPEATABLE_READ
这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。事务在执行期间看到的值必须是一直的 会出现幻读
先看下方的隔离级别场景图,然后看完了后再看这张图
可重复读下,虽然事务A先启事务,但当他在事务B提交后第一次来查询name值后为修改后的lisi的值, 而不是认为事务A先启动先生成快照又是可重复读以为是zhangsan呢,但测试后为lisi 这个要注意
ISOLATION_SERIALIZABLE
这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。 除了防止脏读,不可重复读外,还避免了幻像读 用的很少 严重影响性能
隔离级别的场景
- 读未提交一个事务还没提交时,它做的变更就能其它事物看到
- 读提交一个事务提交之后,它做的变更才会被其他事务看到
- 可重复读一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的 不可见 也可以这样理解 每个事务启动的时候打一个快照,别人改的“我不听我不听” 不可避免出现幻读(比如 拿这个主键查的时候没有记录,当插入时发现有记录了—另一个事物插入的,见鬼了出现幻觉了吗 叫幻读)
- 串行化:加锁事物串行化
隔离级别的原理
事务隔离的实现:
每条记录在更新的时候都会同时记录一条回滚操作。
同一条记录在系统中可以存在多个版本,这就是数据库的多版本并发控制(MVCC)。
回滚日志的删除 系统会判断当没有事务需要用到这些回滚日志的时候,回滚日志才会删除