一、事务的关键对象
事务隔离级别
脏读:
所谓脏读就是一个事务 A 读取另一个事务 B 修改但尚未提交的数据, 并在此基础上操作,而事务 B 又执行事务回滚(也就是撤销了事务), 因为这个数据是还没有提交的数据, 那么事务A读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
不可重复读:
所谓不可重复读就是一个事务对同一行数据重复读取两次,但是却得到了不同的结果。 在这个事务还没有结束时,另外一个事务也访问该同一数据并进行了修改。 那么,在第一个事务中的两次读此数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
幻读:
指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中所有数据的某一列进行修改,同时,第二个事务也修改这个表中的数据, 向表中插入一行新数据。那么,第一个事务的用户修改后查询, 发现表中还有没有修改的数据行(第二个事务插入的新数据),就好象发生了幻觉一样。
1、ISOLATION_DEFAULT: 这是PlatformTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。
2、ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,隔离级别最差,脏读、不可重复读、幻读都不能避免。
3、ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另一个事务读取。另一个事务不能读取该事务未提交的数据。可避免脏读,不可重复读、幻读无法避免。
不可重复读原因:A事务修改,B事务查询,A事务提交前和提交后,B事务看到的数据是不一致的。
幻读原因:A事务修改,B事务新增,B事务提交前,A事务已经提交。B事务提交后,A查询发现仍有数据未修改。
4、ISOLATION_REPEATABLE_READ: 这种隔离级别可以防止脏读,不可重复读。但是可能出现幻读。(MySQL默认的隔离级别)
5、ISOLATION_SERIALIZABLE: 这是花费最高代价但是最可靠的事务隔离级别。事务顺序执行,可避免脏读、不可重复读、幻读,但效率最差。因为A事务执行时,其他事务必须等待。
总结:
自上而下, 四种隔离级别由低到高(读未提交最低, 序列化最大), 隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为 RC(读提交)。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和丢失更新这些并发性问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
大多数数据库的默认级别就是 RC(授权读取、读提交),比如Sql Server , Oracle。MySQL的默认隔离级别就是 RR。
事务的传播行为(Propagation Behavior)
事务的传播行为(Propagation Behavior)是一个什么概念?简单点说就是当一个事务方法被另一个事务方法调用时,传播行为可以控制是否需要创建事务以及如何创建事务,spring-tx 中定义了7种事务传播行为:
1、REQUIRED(默认) 表示方法必须运行在事务中,如果当前事务存在,方法将会在该事务中运行,否则将开启一个新的独立事务。
2、REQUIRES_NEW 创建一个新事务,如果存在当前事务,则挂起该事务
3、NESTED 嵌套事务传播类型,如果当前事务存在,则在嵌套事务中执行,否则开启一个事务
4、NOT_SUPPORTED 以非事务方式执行,如果当前存在事务,则挂起当前事务。
5、SUPPORTS 当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行。
6、NEVER 如果当前没有事务存在,就以非事务方式执行;如果有,就抛出异常。
7、MANDATORY 当前存在事务,则加入当前事务;如果当前事务不存在,则抛出异常。
二、核心事务底层封装实现方法 TransactionAspectSupport
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
// 先去获取事务的属性
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final TransactionManager tm = determineTransactionManager(txAttr);
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager){...}
// 从属性中获取事务管理器, 使用mybatis的话是DataSourceTransactionManager
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 这里执行声明式事务
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 创建一个带有事务信息的对象TransactionInfo
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
// 执行原方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
// 如果有异常就走这里,对异常进行处理
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 如果Info里存在旧的Info,将旧的Info放入当前线程表示将当前Info变为上一个Info
cleanupTransactionInfo(txInfo);
}
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
// 如果程序没有发生异常,就会走到这里,执行提交的操作
commitTransactionAfterReturning(txInfo);
return retVal;
}
// 这里执行编程式事务
else {...}
}TransactionInfo 是存放事务信息的对象, 这个对象里面有一个属性status, 这个对象描述了事务的状态,是一个新事务还是一个旧的事务。
处理事务异常的情况 completeTransactionAfterThrowing
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
// 这里会判断该异常是否回滚,如果没设置的话将默认回滚RuntimeException
// 如果是Exception这里不会回滚的,除非设置了rollbackFor->Exception
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
// 如果该异常需要回滚,则回滚,这里会将Info中的status传入方法
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {...}
catch (RuntimeException | Error ex2) {...}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
// 如果进入这里,说明此异常不需要进行回滚,会照常提交!这里会将Info中status传入方法
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {...}
catch (RuntimeException | Error ex2) {...}
}
}
}首先,我们来看下txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
public final void rollback(TransactionStatus status) throws TransactionException {
// 如果status已经被标记完成了,这里抛异常。
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
processRollback(defStatus, false);
}private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
// 如果此时的status显示是新的事务才进行回滚
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else {
// Participating in larger transaction
// 如果status中有事务,进入下面
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
// 对status中的transaction作一个回滚了的标记
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
// 最后完善status
cleanupAfterCompletion(status);
}
}总结事务异常情况
status.hasSavepoint() 如果status中有savePoint,只回滚到savePoint
status.isNewTransaction() 如果status是一个新事务,才会真正去回滚
status.hasTransaction() 如果status有事务,将会对staus中的事务标记
只有是新的事务才会回滚。
处理事务无异常准备提交的情况 commitTransactionAfterReturning
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}public final void commit(TransactionStatus status) throws TransactionException {
// 如果status已经被标记完成了,这里抛异常。
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus, false);
return;
}
// defStatus.isGlobalRollbackOnly()这个方法将判断status是否被标记回滚了
// 主要依据是transaction中的connHolder是否被标记
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
// 这里会进行回滚,并且抛出一个异常
processRollback(defStatus, true);
return;
}
// 如果没有被标记回滚之类的,这里才真正判断是否提交
processCommit(defStatus);
}private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
// 判断是否是新事务
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
// 此处才真正去提交!
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {...}
catch (TransactionException ex) {...}
catch (RuntimeException | Error ex) {...}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
// 触发事务提交后回调
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
// 最后完善清理status
cleanupAfterCompletion(status);
}
}总结
这里status是新事务,才会进行提交或回滚。
而无论是在异常还是没有异常的流程中,最后的finally块中都会执行一个方法
cleanupAfterCompletion(status)
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
// 设置完成标记,只能 commit/rollback 一次
status.setCompleted();
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}
// 如果status中有挂起的对象 恢复 transaction
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
}
}doCleanupAfterCompletion 完善 transaction
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// Remove the connection holder from the thread, if exposed.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
// Reset connection.
Connection con = txObject.getConnectionHolder().getConnection();
try {
if (txObject.isMustRestoreAutoCommit()) {
con.setAutoCommit(true);
}
DataSourceUtils.resetConnectionAfterTransaction(
con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
if (txObject.isNewConnectionHolder()) {
if (logger.isDebugEnabled()) {
logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
}
DataSourceUtils.releaseConnection(con, this.dataSource);
}
txObject.getConnectionHolder().clear();
}txObject.isNewConnectionHolder() 如果transaction是一个新的holder,将此holder从当前线程解绑!
这里需要了解一个关键线程类,专门存放线程级别的变量TransactionSynchronizationManager
status.getSuspendedResources() != null 如果status中有挂起的对象 恢复 transaction
protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder)
throws TransactionException {
if (resourcesHolder != null) {
Object suspendedResources = resourcesHolder.suspendedResources;
// 如果有被挂起的事务进入
if (suspendedResources != null) {
//真正去恢复的方法
doResume(transaction, suspendedResources);
}
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
if (suspendedSynchronizations != null) {
TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
doResumeSynchronization(suspendedSynchronizations);
}
}
}@Override
protected void doResume(@Nullable Object transaction, Object suspendedResources) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources);
}这里恢复只是把suspendedResources重新绑定到线程中。
1、绑定与解绑围绕一个线程变量,此变量在TransactionSynchronizationManager类中resources, 线程变量就是一个Map这个key是dataSource, 这个value是holder。
2、这里绑定与解绑只需要知道,只是在TransactionSynchronizationManager类中线程变量Map中进行添加与移除的操作而已
总结
事务的大致流程
创建事务Info对象,下面几个步骤都要围绕事务Info对象展开
1、如有异常,根据Info中status状态判断回滚或提交或其他操作
2、如无异常,准备提交也要根据Info中status进行判断正常提交还是会回滚
3、流程结束,可知无论是有异常还是无异常都会进入完善收尾工作,在收尾过程如果是新的Holder将会进行解绑。如果status中存在被挂起的事务,会恢复它,具体恢复其实就是解绑操作。
文章详细阐述了数据库事务的四个隔离级别,包括脏读、不可重复读和幻读的概念,并介绍了Spring中事务的传播行为。通过TransactionAspectSupport类的代码分析,展示了Spring如何处理事务的开始、提交、回滚以及异常处理。同时,提到了TransactionInfo对象在事务管理中的作用,以及在异常和正常流程中对事务状态的判断与操作。

9906

被折叠的 条评论
为什么被折叠?



