Spring 事务实现分析

1. Spring 事务简介

Spring 本身并不实现事务,Spring事务 的本质 还是 底层数据库 对事务的支持,没有 数据库 事务的支持,Spring事务就不会生效。

Spring 事务 提供一套抽象的事务管理,并且结合 Spring IOC 和 Spring AOP,简化了应用程序使用数据库事务,通过声明式事务,可以做到对应用程序无侵入的实现事务功能。例如 使用JDBC 操作数据库,想要使用事务的步骤为:

1、获取连接 Connection con = DriverManager.getConnection()

2、开启事务con.setAutoCommit(true/false);

3、执行CRUD

4、提交事务/回滚事务 con.commit() / con.rollback();

5、关闭连接 conn.close();

采用Spring 事务后,只需要 关注第3步的实现,其他的步骤 都是Spring 完成。

Spring事务的本质 其实就是 AOP 和 数据库事务,Spring 将数据库的事务操作提取为 切面,通过AOP的方式 增强 事务方法。

2. 事务传播行为

事务传播行为类型 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

3. 使用Spring事务

3.1. 通过 PlatformTransactionManager使用(不推荐)

@Test
    public void test12(){
    // 默认的事务定义
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        //开启事务。
        TransactionStatus transaction = transactionManager.getTransaction(defaultTransactionDefinition);
        try {
            Account account = new Account();
            account.setUserName("wokalakala");
            List<Account> accountList = accountMapper.queryAccountList(account);
        }catch (Exception e){
        //事务回滚 
            transactionManager.rollback(transaction);
        }
        //事务提交
        transactionManager.commit(transaction);
    }

3.2. 通过TransactionTemplate 使用事务

@Test
    public void testTransactionTemplate(){
        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        List<Account> accountList = transactionTemplate.execute(status -> {
            Account account = new Account();
            account.setUserName("wokalakala");
            return accountMapper.queryAccountList(account);
        });
    }

3.3. 声明式事务

我们经常使用的 方式,通过AOP 实现,对应用程序 侵入较少,采用注解的方式比较简单方便,省去XML 繁琐的配置。

4. 组件介绍

4.1事务管理 PlatformTransactionManager

PlatformTransactionManager 是 Spring 事务结构中的核心接口,Spring并不直接管理事务,而是提供了多种事务管器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现.Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。此接口的内容如下:

Public interface PlatformTransactionManager(){  
// 由TransactionDefinition得到TransactionStatus对象
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; 
// 提交
Void commit(TransactionStatus status) throws TransactionException;  
// 回滚
Void rollback(TransactionStatus status) throws TransactionException;  
} 

通过PlatformTransactionManager的接口 可以看出,Spring 事务的 的三个核心方法,事务开启 ,提交事务,回滚事务,Spring 事务功能的实现 都是围绕这三个方法来实现。

20160324011156424 (1).jpg

4.2 TransactionInfo

事务信息对象,包括一个事务所有的信息,持有 事务管理器,事务定义对象,目标方法唯一标识,事务状态对象,外层的TransactionInfo,外层的TransactionInfo 用于在应用程序中获取 当前的TransactionInfo 对象。

未命名文件 (2).png

4.3 TransactionStatus

表示一个事务状态,在应用程序中可以通过 TransactionInterceptor.currentTransactionStatus() 的静态函数获取到。

  1. 持有事务对象 ,使用JDBC 事务时 ,事务对象为 DataSourceTransactionObject,
  2. 对保存点的支持,可以在应用程序中 通过 设置保存点 ,在事务回滚时,回滚到 保存点,而不是回滚部分。

4.4 TransactionDefinition

事务定义对象,封装了@Transactional 注解中设置的各种信息,通过收集@Transactional属性信息,获取到 一个 TransationalDefinition ,有了事务定义信息 就可以通过PlatformTransactionManager开启事务 或者 复用一个事务了。

4.5 TransationSynchronization 事务同步回调接口

事务同步回调接口,在事务 周期的各个点 执行回调 方法。比如 挂起 ,继续,提交前后 ,完成前后 。用于 管理 应用程序在事务周期中绑定的资源。在Spring - Mybatis 整合时,正式Mybatis 正式利用了TransationSynchronization同步器,才让Mybatis 的事务管理交给了 Spring 事务来管理。

4.6 TransactionSynchronizationManager 事务同步回调的管理器

在事务运行过程中,需要保存一些状态,比如 数据库连接,

ThreadLocal<Map<Object, Object>> resources - 应用代码随事务的声明周期绑定的对象  
ThreadLocal<Set<TransactionSynchronization>> synchronizations-使用的同步器,用于应用扩展  
ThreadLocal<String> currentTransactionName-事务的名称  
ThreadLocal<Boolean> currentTransactionReadOnly-事务是否是只读  
ThreadLocal<Integer> currentTransactionIsolationLevel-事务的隔离级别  
ThreadLocal<Boolean> actualTransactionActive-是否实际的开启了事务,如果加入 到 别的事务,就不是实际开启事务。

4.7 SuspendedResourceHolder 挂起的资源持有对象

在挂起一个事务时,用于记录被挂起事务的运行时信息,这些信息就是TransactionSynchronizationManager中记录的事务信息。然后将这些信息 保存在 新的DefaultTransactionStatus对象中,便于内部事务运行结束后,恢复外层事务。

5. Spring 事务实现

Spring事务 把 整个事务流程 模板化,采用AOP的形式 增强到 需要事务 的方法,所以 按照 AOP 的 实现 一定存在 一个事务的增强器,这个增强器就是 BeanFactoryTransactionAttributeSourceAdvisor,该增强器中有个环绕通知TransactionInterceptor,所有的事务逻辑 都在这个类的Invoke 方法中,分析Spring 事务实现就从这个函数开始。

  • invoke
//invocation 维护了 AOP 拦截器链 ,执行 invocation.prcess 方法 会沿着拦截器链执行下去直到目标方法。
public Object invoke(final MethodInvocation invocation) throws Throwable {
//获取目标对象
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
        // 执行 父类 TransactionAspectSupport's invokeWithinTransaction...
        return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
            @Override
            public Object proceedWithInvocation() throws Throwable {
            //继续一下执行下拦截器 也可能式目标方法
                return invocation.proceed();
            }
        });
    }
  • TransactionAspectSupport's invokeWithinTransaction
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
            throws Throwable {

1. 准备事务的基本信息
        // If the transaction attribute is null, the method is non-transactional.
        //事务定义 TransactionAttribute 是 TransationDefinition 的子类
        final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
        //获取 事务管理器 ,这里是一个策略模式,根据 事务定义 指定的 事务管理器 获取到 指定的 事务管理器。
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        //连接点  标识
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
            // Standard transaction demarcation with getTransaction and commit/rollback calls.
        2. 开启事务 
        //如果必要 才会开启事务,这里会根据 事务的传播行为 信息 来决定是否开启事务 还是 加入一个已经存在的事务。这里会涉及到事务的挂起 
            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
            Object retVal = null;
            try {
            执行目标方法或者 执行AOP 拦截器链中的下一个拦截器。
                // 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
                3. 事务的回滚 
                //是否回滚 会根据 rollback 属性 判断
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
            清理事务信息
                cleanupTransactionInfo(txInfo);
            }
            4. 提交事务
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }
        }
        //省略部代码

5.1 准备事务

准备事务 主要是做了两件事情

  1. 收集@Transactional注解属性信息,生成 事务定义对象。
    由于@Transactional可以作用在类上 又可以作用在方法上,所以在收集属性信息的时候,就考虑到这种情况。
    AnnotationTransactionAttributeSource 类就是用来解析类和方法上面的@Transactional 注解属性。
    那么到底 先解析类上面的 还是 先解析 方法上面的注解呢?如果方法上面 和 类上面 同时存在呢,是完整替换? 还是取并集?
    定义解析@Transactional 注解的 逻辑定义在 其父类AbstractFallbackTransactionAttributeSource的computeTransactionAttribute,通过查看代码 便可以一目了然:
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
            return null;
        }

        // Ignore CGLIB subclasses - introspect the actual user class.
        Class<?> userClass = ClassUtils.getUserClass(targetClass);
        // The method may be on an interface, but we need attributes from the target class.
        // If the target class is null, the method will be unchanged.
        Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
        // If we are dealing with method with generic parameters, find the original method.
        specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

//首先 解析方法上面 的 属性信息
        // First try is the method in the target class.
        TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    //如果方法上面存在 就返回。
        if (txAttr != null) {
            return txAttr;
        }

//其次 解析作用在类上面的注解属性信息,如果找到 就返回。
        // Second try is the transaction attribute on the target class.
        txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
        if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
            return txAttr;
        }

//解析接口方法上面的注解属性信息 ,如果找到返回。
        if (specificMethod != method) {
            // Fallback is to look at the original method.
            txAttr = findTransactionAttribute(method);
            if (txAttr != null) {
                return txAttr;
            }
            //最后 解析接口上面的注解信息。
            // Last fallback is the class of the original method.
            txAttr = findTransactionAttribute(method.getDeclaringClass());
            if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
                return txAttr;
            }
        }

        return null;
    }

通过上面的代码可以看出来,@Transactional 注解定义在不同位置的 优先级
为 ,实列方法,实列类,接口方法,接口类。不会取并集 ,也不会覆盖,按照优先级查找,直到找到为止。

  • 用到了缓存
    虽然解析注解属性不是那么的耗时,但是也不能每次执行事务方法都要解析一次注解属性,所以在解析注解的时候Spring采用了缓存,这样就只需要一次解析注解,而后的每次执行都会存缓存中获取。这是一个典型的拿空间换时间的列子。采用缓存的代码在其父类 AbstractFallbackTransactionAttributeSource 的getTransactionAttribute函数。

在使用缓存的时候 难免遇到缓存穿透的现象,就是用key 获取缓存的时候 没有 获取到对象,然后就要去解析@Transactional ,结果发现 还是没有,此后的每次调用都会持续这个现象,所以Spring 在发现 不存在的时候 就会定义一个特殊的 value 放到缓存中,以标识 这个已经解析过了,确实不存在。

  • 解析注解的时机
    解析的时机 是在IOC 第一次初始化 Bean的时候,具体点就是 在为目标对象匹配增强器的时候,会触发解析注解。
  1. 获取事务管理器

如果使用@Transactional 指定了使用哪个事务管理器 ,就会获取响应的事务管理器。如果没有 就从IOC 容器中获取。

5.2 开启事务

收集到了事务定义信息,和 事务管理器之后,就可以 利用PlatformTransactionManager.getTransactional 开启事务了,但是开启事务,有很多情况需要考虑,比如繁多的事务传播行为,比如是否已经存在事务,不同的条件都会影响是否要开启一个新事务。有的传播行为 还会设计到 挂起 已经存在的事务。也是相当复杂的。

protected TransactionInfo createTransactionIfNecessary(
            PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {

        // 采用 委托的方式 包装 事务定义 对象.
        if (txAttr != null && txAttr.getName() == null) {
            txAttr = new DelegatingTransactionAttribute(txAttr) {
                @Override
                public String getName() {
                    return joinpointIdentification;
                }
            };
        }

        TransactionStatus status = null;
        if (txAttr != null) {
            if (tm != null) {
                //调用 事务管理器开启事务。
                status = tm.getTransaction(txAttr);
            }
            else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
                            "] because no transaction manager has been configured");
                }
            }
        }
    //封装成事务信息对象。
        return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
    }
AbstractPlatformTransactionManager. getTransaction

这是一个模版方法 ,提供了两个抽象方法 供 子类实现。

该方法主要逻辑

  1. 判断当前线程是否存在事务
  2. 存在事务 根据 事务传播行为 创建事务 或 加入当前事务 或 抛出不支持异常
  3. 不存在事务 判断 传播行为是否 为 TransactionDefinition.PROPAGATION_MANDATORY ,如果是抛出异常
  4. PROPAGATION_REQUIRED,PROPAGATION_REQUIRED_NEW ,TransactionDefinition.PROPAGATION_NESTED创建事务
  5. 不运行在事务中,创建空事务。
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
    //获取事务,从线程绑定的信息中获取 事务,抽象方法 留给子类实现。
   Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
   boolean debugEnabled = logger.isDebugEnabled();
        if (definition == null) {
            // Use defaults if no transaction definition given.
            definition = new DefaultTransactionDefinition();
        }
//  判断是否 已经存在事务
        if (isExistingTransaction(transaction)) {
            //已经存在事务 根据传播行为 创建事务 或者 加入事务
            return handleExistingTransaction(definition, transaction, debugEnabled);
        }

        // 检查超时时间 的设置 是否合法
  
        if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
        }

        //如果传播行为 强制 PROPAGATION_MANDATORY 
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
            throw new IllegalTransactionStateException(
                    "No existing transaction found for transaction marked with propagation 'mandatory'");
        }
    //开启新事务
        else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            SuspendedResourcesHolder suspendedResources = suspend(null);
            if (debugEnabled) {
                logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
            }
            try {
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                //抽象方法 开启事务。留给子类实现。
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);
                return status;
            }
            catch (RuntimeException ex) {
                resume(null, suspendedResources);
                throw ex;
            }
            catch (Error err) {
                resume(null, suspendedResources);
                throw err;
            }
        }
        else {
            // Create "empty" transaction: no actual transaction, but potentially synchronization.
            if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
                logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                        "isolation level will effectively be ignored: " + definition);
            }
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
        }
    }
DataSourceTransactionManager.doBegin

这里实际获取数据库 连接 开启事务的地方,从DataSource 中获取连接,并且设置 自动提交为false ,

  1. 获取 数据库连接
  2. 设置 数据库连接自动提交 为 false,开启事务
  3. 绑定数据库连接到 线程
/**
     * This implementation sets the isolation level but ignores the timeout.
     */
    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
        Connection con = null;

        try {
        //如果当前不存在数据库 
            if (!txObject.hasConnectionHolder() ||
                    txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
                    //获取数据库连接,如果采用数据库连接池 这里就是连接池对象。
                Connection newCon = this.dataSource.getConnection();
                //设置连接到 事务对象中。
                txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
            }
            
            txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
            
            con = txObject.getConnectionHolder().getConnection();
//记录上一个 事务的隔离级别 ,如果没有外层事务 ,隔离级别就是null
            Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
            txObject.setPreviousIsolationLevel(previousIsolationLevel);

            //设置自动提交 为false,如果使用 连接池,连接池 或许已经设置自动提交为false了,所以这里先判断一下。
            if (con.getAutoCommit()) {
                txObject.setMustRestoreAutoCommit(true);
                if (logger.isDebugEnabled()) {
                    logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
                }
                con.setAutoCommit(false);
            }
//如果事务是只读事务 ,那么就会执行 SQL "SET TRANSACTION READ ONLY".
            prepareTransactionalConnection(con, definition);
            txObject.getConnectionHolder().setTransactionActive(true);

            int timeout = determineTimeout(definition);
            if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
                txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
            }

            // 如果是一个新的连接 ,绑定数据库连接到当前线程
            if (txObject.isNewConnectionHolder()) {
            //调用 事务同步回调管理器 的 绑定资源方法,key= dataSource ,key = ConnectionHodler
                TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
            }
        }
        catch (Throwable ex) {
            if (txObject.isNewConnectionHolder()) {
            //异常之后 释放连接,
                DataSourceUtils.releaseConnection(con, this.dataSource);
                txObject.setConnectionHolder(null, false);
            }
            throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
        }
    }
事务挂起

当线程中已经存在事务,在某些事务传播行为下就需要挂起外层事务,
比如PROPAGATION_NOT_SUPPORTED:不能运行在一个事务中,如果存在事务就挂起当前事务,执行。
PROPAGATION_REQUIRES_NEW: 必须运行在一个新事务中,如果当前存在事务,则挂起当前事务,开启新事务执行。
如何实现挂起一个事务呢?
挂起事务需要完成几项工作:
1.TransactionSynchronizationManager 中解除绑定的 TransactionSynchronization 集合
2.重置当前事务名称绑定
3.重置事务只读属性绑定
4.重置事务隔离级别绑定
5.重置实际事务激活标志绑定
6.记录以上几部的数据,封装到 SuspendedResourceHolder对象中。
7.将SuspendedResourceHolder对象,交给内部事务 ,以便内部事务执行结束后,恢复外层事务。

事务恢复

如果内部事务出现异常或者 内部事务提交 都会触发外层事务的恢复,事务的恢复就是将内存事务TransactionStauts 中记录的挂起事务的信息,重新绑定到 TransactionSynchronizationManager中去。

5.3 事务回滚

如果事务运行过程中出现某些异常 会导致事务回滚,在JDBC中 我们执行connection.rollback()回滚事务,Spring事务也不列外,只是Spring 事务 在JDBC 的基础之上提供了更多丰富的功能,比如 指定某些异常进行回滚。
关于事务回滚rollback 设置,还有一个容易被忽视 和 误解的地方。就是如果我们设置rollbackFor = IllegalArgumentException.class 那么事务运行期间出现了IndexOutOfBoundsException异常会不会导致事务回滚?出现了 Error 错误会不会回滚?

处理事务回滚的在TransactionAspectSupport . completeTransactionAfterThrowing 函数中

  1. 首先判断 异常是否需要回滚。判断逻辑 最终是委托给 RuleBasedTransactionAttribute.rollbackOn
    public boolean rollbackOn(Throwable ex) {

        RollbackRuleAttribute winner = null;
        int deepest = Integer.MAX_VALUE;
        if (this.rollbackRules != null) {
        //遍历 查找 指定的 rollbackException 进行匹配
            for (RollbackRuleAttribute rule : this.rollbackRules) {
                int depth = rule.getDepth(ex);
                if (depth >= 0 && depth < deepest) {
                    deepest = depth;
                    winner = rule;
                }
            }
        }

        // 如果没有匹配到 采用默认的回滚规则进行判断。
        //默认的规则就是 ex instanceof RuntimeException || ex instanceof Error,所以 如果我们指定了rollback = IllegalArgumentException,当遇到 IndexOutOfBoundsException时 或者 Error 时也会回滚事务。
        if (winner == null) {
            logger.trace("No relevant rollback rule found: applying default rules");
            return super.rollbackOn(ex);
        }

        return !(winner instanceof NoRollbackRuleAttribute);
    }
  1. 如果需要回滚则会执行 AbstranctPlatformTransactionManager.processRollback函数
    2.1 if (status.hasSavepoint())
    如果存在保存点 则回滚到 保存点
    2.2 else if (status.isNewTransaction())
    如果 是一个新事务 执行回滚。
    2.3 . else if (status.hasTransaction())
    如果 是嵌套事务 设置 当前数据库链接 rollbackOnly

  2. 如果不需要回滚 则提交事务

  3. 触发钩子函数
    在回滚前后 会分别触发 TransactionSynchronuzation 的beforeCompletion,afterCompletion 函数,进行资源释放,连接关闭等。

5.4 事务提交

只有当事务是一个 新事务的时候才会进行提交,就是如果 有一个 内嵌事务传播 行为 PROPAGATION_SUPPORTS ,PROPAGATION_REQUIRED ,PROPAGATION_MANDATORY 的事务 执行完之后 不会 提交,会随着外层事务的提交而提交。

事务的提交 最终 是调用 connect.commit 函数提交事务。

在事务提交前后 会触发TransactionSynchronuzation 钩子函数。进行资源释放操作。

mybatis 会在beforeCommit 中 执行Sqlsession commit

6 一些细节

  1. 方法上面 @Transaction 注解 会覆盖 类上面的 @Transaction注解信息。是完全的覆盖,而不是 部分覆盖,就是说 ,如果类上设置了 事务超时时间 为 10秒,但是 方法上面没有设置事务超时时间,那么 最终 事务 就是没有超时时间,并不会 采用 类上面的 超时时间。
  1. 事务隔离级别 和 超时时间 只能作用于 一个 新事务,也就是说 ,当内部事务参与到一个已经存在的事务中时,事务隔离级别 和 超时时间将会被忽略。因为 内部事务 是参与到外层事务。

  2. 事务rollbackFor 的 含义是 默认异常 或 指定异常,就是说,默认回滚异常时 runtimeException 或 Error 或 自己指定的异常。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容

  • Spring 事务属性分析 事务管理对于企业应用而言至关重要。它保证了用户的每一次操作都是可靠的,即便出现了异常的...
    壹点零阅读 1,238评论 0 2
  • 很多人喜欢这篇文章,特此同步过来 由浅入深谈论spring事务 前言 这篇其实也要归纳到《常识》系列中,但这重点又...
    码农戏码阅读 4,651评论 2 59
  • 这部分的参考文档涉及数据访问和数据访问层和业务或服务层之间的交互。 Spring的综合事务管理支持覆盖很多细节,然...
    竹天亮阅读 1,010评论 0 0
  • 在大叻,每天都有一场雨。有时从中午落至下午,有时,一直绵延到傍晚。 雨水,让这个海拔近1500米的山中小城几乎没有...
    才人没有人阅读 255评论 2 3
  • 感恩一:感恩我们辅导班来了一个极其调皮捣蛋的我之前曾经劝退的男孩子。也许上帝是通过这个孩子来修炼我的。感恩孩子的到...
    杜辅教育孙瑞敏阅读 125评论 0 1