基于springboot的Mybatis源码分析 -----------------2 sql执行顺序

56 篇文章 2 订阅

可以看到Mapper是一个MapperProxy的代理类

 执行mapper的方法最终会进入MapperProxy的invoke方法

进入MapperMethods#execute。以select为例

result = sqlSession.selectOne(command.getName(), param);这个sqlSession是一个SqlSessionTemplate
SqlSessionTemplate#selectOne
public <T> T selectOne(String statement, Object parameter) {
    return this.sqlSessionProxy.selectOne(statement, parameter);
}

sqlSessionProxy也是被代理了,这个方法会进入

SqlSessionInterceptor#invoke方法

首先会获取到一个sqlSession,这个sqlSession是DefaultSqlSession。接着就会真正调用

DefaultSqlSession的selectOne方法。这里先跳过

之所以spring会用代理包了一层,就是因为后面几句话。spring用自己的事务管理器来管理事务。

if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
    sqlSession.commit(true);
}

如果没有事务,那么就会直接提交事务。并且最后会直接销毁sql,

SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);

进入这个方法:

public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
    Assert.notNull(session, "No SqlSession specified");
    Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
    SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
    if (holder != null && holder.getSqlSession() == session) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
        }

        holder.released();
    } else {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
        }

        session.close();
    }

}

如果没有开启事务,会走到else分支,session是被关闭了

所以当我们没有开启事务的时候,在一个service中执行个数据库操作,是属于三个不同的sqlSession。

如果我们开启事务,

SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);

方法会进入if分支。这里并没有关闭session

我们找到

SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);

点击进去

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
    Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
    Assert.notNull(executorType, "No ExecutorType specified");
    SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
        return session;
    } else {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Creating a new SqlSession");
        }

        session = sessionFactory.openSession(executorType);
        registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
        return session;
    }
}

发现sqlSession会先从缓存中获取。

----------------

解释了半天,其实就是说明一个现象,如果开启了事务,那么只要是一个service中的所有数据库操作都属于一个sqlsession,反之则不是。。这个很容易验证,开启debug日志模式,看一看打出的sql就知道了,如果都用一个sqlSession,同样的sql是可以利用一级缓存的。

----------------------------

话说回来,接着看找到DefaultSqlSession的#selectList方法

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    MappedStatement ms = configuration.getMappedStatement(statement);
    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

首先会根据id获取到Mappedstatment,其实就可以理解成sql。然后用executor去执行。mybatis中的Executor有一个装饰器模式在里面,这这里会进入CacheExecutor

BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

很明显,CacheExecutor对SimpleExecutor的功能进行了增强

进入query方法

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
  Cache cache = ms.getCache();
  if (cache != null) {
    flushCacheIfRequired(ms);
    if (ms.isUseCache() && resultHandler == null) {
      ensureNoOutParams(ms, boundSql);
      @SuppressWarnings("unchecked")
      List<E> list = (List<E>) tcm.getObject(cache, key);
      if (list == null) {
        list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        tcm.putObject(cache, key, list); // issue #578 and #116
      }
      return list;
    }
  }
  return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

----->

BaseExecutor#query ----->

  BaseExecutor#queryFromDatabase------------>
    SimpleExecutor#doQuery-------------->

               Configuration#newStatementHandler

可以看到这里会处理所有的插件

statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
public Object pluginAll(Object target) {
  for (Interceptor interceptor : interceptors) {
    target = interceptor.plugin(target);
  }
  return target;
}

SimpleExecutor#prepareStatement

Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);

首先会拿到连接,。然后通过statmentHandler获取到一个Satament,所以我们就可以吧statmentHandler理解成获取到stament的一个工具。有点handlerMapping的味道,,

---------------->

PreparedStatementHandler#query
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  return resultSetHandler.<E> handleResultSets(ps);
}

这个ps也是个代理,execute方法会进入

PreparedStatementLogger#invoke方法。应该就是和日志相关吧,这里不是很清楚

最终会进入

ClientPreparedStatement#execute 方法去执行数据库操作,

回到PreparedStatementHandler#query方法:

resultSetHandler.<E> handleResultSets(ps);

当数据放回数据之后,就会用ResultHandler去处理返回的数据了,这里具体就不详细说明了。。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值