Spring事务处理详解

1.spring与事务处理

事务管理对于企业应用而言至关重要。它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性。在很多情况下,不会只有一个单独的数据库操作,而是一组数据库操作。在这个处理过程中,涉及事务处理单元划分的问题,Spring借助IoC容器的强大配置能力,为应用提供了声明式的事务划分方式。有了spring的支持,只需要一些简单的配置,应用就能完成复杂的事务处理工作,提供很大的方便。

2.Spring声明式事务处理

Spring事务处理模块是通过AOP功能来实现声明式事务处理的,比如事务的配置等。我们从TransactionProxyFactoryBean入手,通过代码来了解spring是如何通过AOP来完成事务配置的。

public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean implements BeanFactoryAware {
 //设置TransactionInterceptor,发挥aop的作用
 private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
 private Pointcut pointcut;
 
 public TransactionProxyFactoryBean() {
    }
    //注入PlatformTransactionManager
    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionInterceptor.setTransactionManager(transactionManager);
 }
    //注入事务属性
    //把属性注入到TransactionInterceptor中
    public void setTransactionAttributes(Properties transactionAttributes) {
        this.transactionInterceptor.setTransactionAttributes(transactionAttributes);
 }
 
    public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
        this.transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);
 }
 
    public void setPointcut(Pointcut pointcut) {
        this.pointcut = pointcut;
 }
 
    public void setBeanFactory(BeanFactory beanFactory) {
        this.transactionInterceptor.setBeanFactory(beanFactory);
 }
 
    protected Object createMainInterceptor() {
//afterPropertiesSet是完成aop配置的地方
        this.transactionInterceptor.afterPropertiesSet();
//通知DefaultPointcutAdvisor或者TransactionAttributeSourceAdvisor
 return this.pointcut != null?new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor):new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
 }
}

然后进行事务的配置读入,首先读到TransactionAttributeSource中。NameMatchTransactionAttributeSource作为TransactionAttributeSource的具体实现,是实际完成属性读入和匹配的地方,会读取事务的方法名和属性,把他们作为键值对放入到nameMap中。在调用目标方法的时候,因为这个目标方法已经被TransactionProxyFactoryBean代理,先会判断这个方法是不是事务方法(以方法名为索引在nameMap中查找,使用PatternMatchUtils.simpleMatch进行模式匹配)进行模式匹配的原因是,可以使用通配符*配置事务。所以,如果方法名没能匹配上,而通过模式匹配能匹配上,也要进行事务处理。对应的事务属性也从nameMap中读取出来,触发事务处理拦截器进行拦截。

public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
//对方法进行判断,看是不是事务方法,如果是,就取出相应的属性
String methodName = method.getName(); //直接匹配
TransactionAttribute attr = (TransactionAttribute)this.nameMap.get(methodName);
//模式匹配
if(attr == null) {
String bestNameMatch = null;
Iterator i$ = this.nameMap.keySet().iterator();

while(i$.hasNext()) {
String mappedName = (String)i$.next();
if(this.isMatch(methodName, mappedName) && (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
attr = (TransactionAttribute)this.nameMap.get(mappedName);
bestNameMatch = mappedName;
}
}
}

return attr;
}

之后,在事务处理拦截器TransactionInterceptor中,通过invoke方法对aop的代理对象进行回调。首先的到调用方法的事务配置,然后会取得PlatformTransactionManager,通过这个事务处理器来完成事务的创建、提交、回滚操作。

 2.1事务的创建

//TransactionInterceptor的invoke方法中调用invokeWithinTransaction中使用createTransactionIfNecessary方法。
protected TransactionAspectSupport.TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, final TransactionAttribute txAttr, final String joinpointIdentification) {
//获取事务的配置信息,如果没有指定名字,使用方法作为事务名
    if(txAttr != null && ((TransactionAttribute)txAttr).getName() == null) {
        txAttr = new DelegatingTransactionAttribute((TransactionAttribute)txAttr) {
            public String getName() {
                return joinpointIdentification;
            }
        };
    }
//status封装了事务的状态信息
    TransactionStatus status = null;
    if(txAttr != null) {
        if(tm != null) {
//事务的创建使用处理器完成的,返回事务的状态
            status = tm.getTransaction((TransactionDefinition)txAttr);
        } else if(this.logger.isDebugEnabled()) {
            this.logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured");
        }
    }
//返回TransactionInfo,包含了事务配置以及status
    return this.prepareTransactionInfo(tm, (TransactionAttribute)txAttr, joinpointIdentification, status);
}
 
protected TransactionAspectSupport.TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm, TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {
    TransactionAspectSupport.TransactionInfo txInfo = new TransactionAspectSupport.TransactionInfo(tm, txAttr, joinpointIdentification);
    if(txAttr != null) {
        if(this.logger.isTraceEnabled()) {
            this.logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
        }
//为txInfo设置status,status包含事务transtation对象
        txInfo.newTransactionStatus(status);
    } else if(this.logger.isTraceEnabled()) {
        this.logger.trace("Don\'t need to create transaction for [" + joinpointIdentification + "]: This method isn\'t transactional.");
    }
//把当前的txInfo与线程绑定,同时保存以前的txInfo。这样就有了一连串的txInfo,虽然不一定需要创建事务,但是总会创建txInfo
    txInfo.bindToThread();
    return txInfo;
}

由tm.getTransaction((TransactionDefinition)txAttr)调用触发,创建了一个TransactionStatus对象,在AbstractPlatformTransactionManager中提供了创建事务的模板。

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
//doGetTransaction是个抽象函数,具体实现在具体的事务处理器如datasourcetransactionmanager中
    Object transaction = this.doGetTransaction();
    boolean debugEnabled = this.logger.isDebugEnabled();
//如果没有任何数据定义,就用默认的定义
    if(definition == null) {
        definition = new DefaultTransactionDefinition();
    }
//当前存在事务,需要考虑事务的传播属性
    if(this.isExistingTransaction(transaction)) {
        //这个方法对于理解事务的传播属性很有帮助(后边讲)
        return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled);
    } else if(((TransactionDefinition)definition).getTimeout() < -1) {   //检查超时时间合理性
        throw new InvalidTimeoutException("Invalid transaction timeout", ((TransactionDefinition)definition).getTimeout());
    } 
//当前没有事务,需要根据属性来创建事务,这些数字定义代表传播属性,定义在TransactionDefinition接口中
   else if(((TransactionDefinition)definition).getPropagationBehavior() == 2) {  
        throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation \'mandatory\'");
    } else if(((TransactionDefinition)definition).getPropagationBehavior() != 0 && ((TransactionDefinition)definition).getPropagationBehavior() != 3 && ((TransactionDefinition)definition).getPropagationBehavior() != 6) {
        boolean newSynchronization1 = this.getTransactionSynchronization() == 0;
        return this.prepareTransactionStatus((TransactionDefinition)definition, (Object)null, true, newSynchronization1, debugEnabled, (Object)null);
    } else {
        AbstractPlatformTransactionManager.SuspendedResourcesHolder newSynchronization = this.suspend((Object)null);
        if(debugEnabled) {
            this.logger.debug("Creating new transaction with name [" + ((TransactionDefinition)definition).getName() + "]: " + definition);
        }
 
        try {
            boolean err = this.getTransactionSynchronization() != 2;
            DefaultTransactionStatus status = this.newTransactionStatus((TransactionDefinition)definition, transaction, true, err, debugEnabled, newSynchronization);
 
//创建事务,具体由事务处理器实现
            this.doBegin(transaction, (TransactionDefinition)definition);
            this.prepareSynchronization(status, (TransactionDefinition)definition);
            return status;
        } catch (RuntimeException var7) {
            this.resume((Object)null, newSynchronization);
            throw var7;
        } catch (Error var8) {
            this.resume((Object)null, newSynchronization);
            throw var8;
        }
    }
}

在这个模板的基础上,具体的事务的创建需要事务处理器来完成。要实现doBegin方法。事务的创建会生成一个TransactionStatus 对象,是TransactionInfo的一个属性,TransactionInfo保存在ThreadLocl中,这样当前线程就能从Threadlocal中获取当前的事务。

在 Spring 中,事务是通过 TransactionDefinition 接口来定义的。该接口包含与事务属性有关的方法。具体如下所示:
TransactionDefinition 接口中定义的主要方法
public interface TransactionDefinition{

int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;

int getPropagationBehavior();

int getIsolationLevel();

int getTimeout();

boolean isReadOnly();

String getName();

} 

可以看到,接口只提供了获取属性的方法,而没有提供相关设置属性的方法。其实道理很简单,事务属性的设置完全是由我们控制的,因此可以自定义任何设置属性的方法,而且保存属性的字段也没有任何要求。唯一的要求的是,Spring 进行事务操作的时候,通过调用以上接口提供的方法必须能够返回事务相关的属性取值。

 

当创建事务的时候,已经存在事务了。这个处理在handleExistingTransaction中:

private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException {
    if(definition.getPropagationBehavior() == 5) {
        throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation \'never\'");
    } else {
        AbstractPlatformTransactionManager.SuspendedResourcesHolder newSynchronization2;
        boolean isoConstants2;
        if(definition.getPropagationBehavior() == 4) {
            if(debugEnabled) {
                this.logger.debug("Suspending current transaction");
            }
 
            newSynchronization2 = this.suspend(transaction);
            isoConstants2 = this.getTransactionSynchronization() == 0;
            return this.prepareTransactionStatus(definition, (Object)null, false, isoConstants2, debugEnabled, newSynchronization2);
        } else if(definition.getPropagationBehavior() == 3) {
            if(debugEnabled) {
                this.logger.debug("Suspending current transaction, creating new transaction with name [" + definition.getName() + "]");
            }
 
            newSynchronization2 = this.suspend(transaction);
 
            try {
                isoConstants2 = this.getTransactionSynchronization() != 2;
                DefaultTransactionStatus status = this.newTransactionStatus(definition, transaction, true, isoConstants2, debugEnabled, newSynchronization2);
                this.doBegin(transaction, definition);
                this.prepareSynchronization(status, definition);
                return status;
            } catch (RuntimeException var7) {
                this.resumeAfterBeginException(transaction, newSynchronization2, var7);
                throw var7;
            } catch (Error var8) {
                this.resumeAfterBeginException(transaction, newSynchronization2, var8);
                throw var8;
            }
        } else {
            boolean newSynchronization1;
            if(definition.getPropagationBehavior() == 6) {
                if(!this.isNestedTransactionAllowed()) {
                    throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions by default - specify \'nestedTransactionAllowed\' property with value \'true\'");
                } else {
                    if(debugEnabled) {
                        this.logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
                    }
 
                    if(this.useSavepointForNestedTransaction()) {
                        DefaultTransactionStatus newSynchronization3 = this.prepareTransactionStatus(definition, transaction, false, false, debugEnabled, (Object)null);
                        newSynchronization3.createAndHoldSavepoint();
                        return newSynchronization3;
                    } else {
                        newSynchronization1 = this.getTransactionSynchronization() != 2;
                        DefaultTransactionStatus isoConstants1 = this.newTransactionStatus(definition, transaction, true, newSynchronization1, debugEnabled, (Object)null);
                        this.doBegin(transaction, definition);
                        this.prepareSynchronization(isoConstants1, definition);
                        return isoConstants1;
                    }
                }
            } else {
                if(debugEnabled) {
                    this.logger.debug("Participating in existing transaction");
                }
 
                if(this.isValidateExistingTransaction()) {
                    if(definition.getIsolationLevel() != -1) {
                        Integer newSynchronization = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
                        if(newSynchronization == null || newSynchronization.intValue() != definition.getIsolationLevel()) {
                            Constants isoConstants = DefaultTransactionDefinition.constants;
                            throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] specifies isolation level which is incompatible with existing transaction: " + (newSynchronization != null?isoConstants.toCode(newSynchronization, "ISOLATION_"):"(unknown)"));
                        }
                    }
 
                    if(!definition.isReadOnly() && TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                        throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] is not marked as read-only but existing transaction is");
                    }
                }
 
                newSynchronization1 = this.getTransactionSynchronization() != 2;
                return this.prepareTransactionStatus(definition, transaction, false, newSynchronization1, debugEnabled, (Object)null);
            }
        }
    }
}

事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
* TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
* TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
* TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
* TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
* TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
事务传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
* TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
* TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
* TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
* TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
* TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
* TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
* TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。

 2.2事务的提交

事务的提交在TransactionInterceptor中的invokeWithinTransaction中,同样在AbstractPlatformTransactionManager中提供了模板。

public final void commit(TransactionStatus status) throws TransactionException {
//在TransactionStatus中表示事务已经结束了
    if(status.isCompleted()) {
        throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");
    } else {
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;
//出现异常
        if(defStatus.isLocalRollbackOnly()) {
            if(defStatus.isDebug()) {
                this.logger.debug("Transactional code has requested rollback");
            }
//回滚
            this.processRollback(defStatus);
        } else if(!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
            if(defStatus.isDebug()) {
                this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
            }
 
            this.processRollback(defStatus);
//其实因为事务嵌套,serviceA#methodA中有异常标记并直接回滚,serviceB#methodB中有异常只是标记回滚状态,在调用回serviceA#methodA中回滚) 这个方法中serviceB#methodB有异常事务被标记为
//回滚,继续执行到serviceA.methodA() 可是被methodA捕获了,也就不回滚了,一直执行到最后commit。在commit时spring会判断回滚标志,若有回滚标记,回滚并抛出UnexpectedRollbackException
     if(status.isNewTransaction() || this.isFailEarlyOnGlobalRollbackOnly()) {
                throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
            }
        } else {
            this.processCommit(defStatus);
        }
    }
}
//提交事务的入口
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    try {
        boolean beforeCompletionInvoked = false;
 
        try {
            this.prepareForCommit(status);
            this.triggerBeforeCommit(status);
            this.triggerBeforeCompletion(status);
            beforeCompletionInvoked = true;
            boolean err = false;
            if(status.isNewTransaction() || this.isFailEarlyOnGlobalRollbackOnly()) {
                err = status.isGlobalRollbackOnly();
            }
//嵌套事务的处理
            if(status.hasSavepoint()) {
                if(status.isDebug()) {
                    this.logger.debug("Releasing transaction savepoint");
                }
 
                status.releaseHeldSavepoint();
            }
//判断当前事务的状态,如果新事务,直接提交。如果不是,则不提交,交由已经存在的事务提交
 else if(status.isNewTransaction()) {
                if(status.isDebug()) {
                    this.logger.debug("Initiating transaction commit");
                }
 
                this.doCommit(status);
            }
 
            if(err) {
                throw new UnexpectedRollbackException("Transaction silently rolled back because it has been marked as rollback-only");
            }
        } catch (UnexpectedRollbackException var19) {
            this.triggerAfterCompletion(status, 1);
            throw var19;
        } catch (TransactionException var20) {
            if(this.isRollbackOnCommitFailure()) {
                this.doRollbackOnCommitException(status, var20);
            } else {
                this.triggerAfterCompletion(status, 2);
            }
 
            throw var20;
        } catch (RuntimeException var21) {
            if(!beforeCompletionInvoked) {
                this.triggerBeforeCompletion(status);
            }
 
            this.doRollbackOnCommitException(status, var21);
            throw var21;
        } catch (Error var22) {
            if(!beforeCompletionInvoked) {
                this.triggerBeforeCompletion(status);
            }
 
            this.doRollbackOnCommitException(status, var22);
            throw var22;
        }
 
        try {
            this.triggerAfterCommit(status);
        } finally {
            this.triggerAfterCompletion(status, 0);
        }
    } finally {
        this.cleanupAfterCompletion(status);
    }
 
}

2.3事务的回滚

事务的回滚和事务的提交非常的相似,AbstractPlatformTransactionManager中的processRollback方法 提供了回滚的模板。

private void processRollback(DefaultTransactionStatus status) {
    try {
        try {
            this.triggerBeforeCompletion(status);
//嵌套事务的回滚
            if(status.hasSavepoint()) {
                if(status.isDebug()) {
                    this.logger.debug("Rolling back transaction to savepoint");
                }
 
                status.rollbackToHeldSavepoint();
            } 
//新建事务的回滚
            else if(status.isNewTransaction()) {
                if(status.isDebug()) {
                    this.logger.debug("Initiating transaction rollback");
                }
 
                this.doRollback(status);
            } 
//没有新创建的事务的回滚
else if(status.hasTransaction()) {
                if(!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) {
                    if(status.isDebug()) {
                        this.logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
                    }
                } else {
                    if(status.isDebug()) {
                        this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                    }
 
                    this.doSetRollbackOnly(status);
                }
            } else {
                this.logger.debug("Should roll back transaction but cannot - no transaction available");
            }
        } catch (RuntimeException var7) {
            this.triggerAfterCompletion(status, 2);
            throw var7;
        } catch (Error var8) {
            this.triggerAfterCompletion(status, 2);
            throw var8;
        }
 
        this.triggerAfterCompletion(status, 1);
    } finally {
        this.cleanupAfterCompletion(status);
    }
 
}

3.我们应该注意什么

1.事务的传播属性

2.事务的回滚

3.超时时间设置

4.隔离级别设置

5.两种配置方式的优缺点

 


转载于:https://my.oschina.net/zouqun/blog/405408

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值