Spring源码解析---事务管理(1)

当我们没有开启事务时,与直接使用mybatis不同,spring默认的aotucommit是true,所以spring模式下每次的操作默认都会提交,这样每次我们的操作在未使用缓存时,都是会获取到不同的connnection,而事务的开启会改变这一切。

        在非事务环境下我们可以看源码知道获取sqlsession的过程,spring为我们提供了一个sqlsession的模板类,sqlsessionTemplet,每一个mapper都会有自己对应的模板类实例,他的内部类SqlSessionInterceptor将使用jdk动态代理的方式代理该模板

 通过方法压栈可以看出在调用了模板的selectOne之后调用了进入了代理类,最后执行了invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);

            Object unwrapped;
            try {
                Object result = method.invoke(sqlSession, args);
                if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                    sqlSession.commit(true);
                }

                unwrapped = result;
            } catch (Throwable var11) {
                unwrapped = ExceptionUtil.unwrapThrowable(var11);
                if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                    sqlSession = null;
                    Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }

                throw (Throwable)unwrapped;
            } finally {
                if (sqlSession != null) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                }

            }

            return unwrapped;
        }

在执行invoke方法时会通过sqlsessionUtils获取sqlsession

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
        Assert.notNull(executorType, "No ExecutorType specified");
//获取当前事务中保存的SqlsessionHolder,只有在开启事务的情况下,才会在后续对sqlsession
//进行注册,否则将永远拿到的是一个新的sqlsession,那么一级缓存将一直失效
        SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
//从sqlsessionHolder中获取到sqlsession
        SqlSession session = sessionHolder(executorType, holder);
        if (session != null) {
            return session;
        } else {
            LOGGER.debug(() -> {
                return "Creating a new SqlSession";
            });
//sqlsession如果没有保存在sqlsessionHolder中则会重新创建一个
            session = sessionFactory.openSession(executorType);
//在第一次或之前没有缓存的sqlsession情况下创建了sqlsession后会将其注册到sqlsessionHolder中
            registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
            return session;
        }
    }


private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
//在注册sqlsession时,会对当前线程是否开启事务进行检测,如果没有开启事务就不会对sqlsession
//进行注册
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            //省略部分.......
        }

    }

这是在开启事务后sqlsession上与非事务的主要区别,那么对于connection在不同的时机采用了类似的手段。 

首先看获取connnection之前的栈区信息

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
        Assert.notNull(dataSource, "No DataSource specified");
// ConnectionHolder中保存了当前事务中的connnection,
//不同于sqlsession是和sqlsessionFactory绑定在一起,connnection是和datasource绑定的,
//是为了区分不同的数据源
        ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
        if (conHolder == null || !conHolder.hasConnection() && !conHolder.isSynchronizedWithTransaction()) {
            logger.debug("Fetching JDBC Connection from DataSource");

//从数据源中获取connection
            Connection con = fetchConnection(dataSource);
            if (TransactionSynchronizationManager.isSynchronizationActive()) {
                try {
                    ConnectionHolder holderToUse = conHolder;
                    if (conHolder == null) {
                        holderToUse = new ConnectionHolder(con);
                    } else {
                        conHolder.setConnection(con);
                    }

                    holderToUse.requested();
                    TransactionSynchronizationManager.registerSynchronization(new DataSourceUtils.ConnectionSynchronization(holderToUse, dataSource));
                    holderToUse.setSynchronizedWithTransaction(true);
                    if (holderToUse != conHolder) {
                        TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
                    }
                } catch (RuntimeException var4) {
                    releaseConnection(con, dataSource);
                    throw var4;
                }
            }

            return con;
        } else {
            conHolder.requested();
            if (!conHolder.hasConnection()) {
                logger.debug("Fetching resumed JDBC Connection from DataSource");
                conHolder.setConnection(fetchConnection(dataSource));
            }

            return conHolder.getConnection();
        }
    }

当事务开启时,spring是通过动态代理对像日常项目的service层实例进行cglib代理,那么会在调用service方法时对事务可以拦截的方法进行代理,在执行target的方法前datasourceTransactionMannager会在本线程中创建出一个connectionHolder和datasource绑定在一起,这样在事务中就可以保证connection获取到的是同一个。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值