springboot中mybatis接口调用流程

前言

现在有不少的项目采用了springboot+springmvc+mybaits,我们都知道mybatis存在以一级缓存和二级缓存,这里先不谈二级缓存,主要了解一下一级缓存,旨在用一级缓存提升查询性能,一级缓存可以配置session有效和statement有效,默认是session有效。
然后关于mybatis的mapper接口类我们又是通过@Autowared的形式注入的,那么一条数据处理链路中多个Autowared进来的mapper会公用同一个session吗?

一、前情提要

先贴一张我以前阅读mybatis源码的mapper载入流程图


springboot中mybatis接口调用流程_第1张图片
image

二、流程详解

2.1 处理流程图

springboot中mybatis接口调用流程_第2张图片
image.png

2.2 源码分析

2.2.1 code--1

到这里我们已经知道@Autowared注解引入的mapper类其实是MapperProxy对象

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
   if (Object.class.equals(method.getDeclaringClass())) {
     return method.invoke(this, args);
   } else if (isDefaultMethod(method)) {
     return invokeDefaultMethod(proxy, method, args);
   }
  } catch (Throwable t) {
  throw ExceptionUtil.unwrapThrowable(t);
  }
  // 找到容器中的对应mapper中的方法,然后调用它的execute方法
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

2.2.2 code--2

MapperMethod对各种类型的sql做分流处理,这里我们的方法是select,所以直接查看case SELECT

public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  switch (command.getType()) {
   case INSERT: {
   Object param = method.convertArgsToSqlCommandParam(args);
     result = rowCountResult(sqlSession.insert(command.getName(), param));
     break;
   }
   case UPDATE: {
     Object param = method.convertArgsToSqlCommandParam(args);
     result = rowCountResult(sqlSession.update(command.getName(), param));
     break;
   }
   case DELETE: {
     Object param = method.convertArgsToSqlCommandParam(args);
     result = rowCountResult(sqlSession.delete(command.getName(), param));
     break;
   }
   case SELECT:
     if (method.returnsVoid() && method.hasResultHandler()) {
       executeWithResultHandler(sqlSession, args);
       result = null;
     } else if (method.returnsMany()) {
       result = executeForMany(sqlSession, args);
     } else if (method.returnsMap()) {
       result = executeForMap(sqlSession, args);
     } else if (method.returnsCursor()) {
       result = executeForCursor(sqlSession, args);
     } else {
     // 组装参数对象,然后调用sqlsession里面的方法,在后面有展开
       Object param = method.convertArgsToSqlCommandParam(args);
       result = sqlSession.selectOne(command.getName(), param);
     }
     break;
   case FLUSH:
     result = sqlSession.flushStatements();
     break;
   default:
     throw new BindingException("Unknown execution method for: " + command.getName());
  }
  if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
   throw new BindingException("Mapper method '" + command.getName()
       + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  }
  return result;
}

2.2.3 code--3

SqlSessionTemplate

@Override
public  T selectOne(String statement, Object parameter) {
  // 在看这里的源码的时候,有点瞎,怎么这里调用的是selectone,但是却调用到了SqlSessionInterceptor的invoke
  // 后来发现想错了,这里用的接口代理,然后再代理里面处理事务,session的关闭等操作。
  return this.sqlSessionProxy. selectOne(statement, parameter);
}
// 在SqlSessionTemplate内部的一个接口代理实现类,这里是一个流程中的重点
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  // 获取sqlSession,下面有方法源码,详见code--4
  SqlSession sqlSession = getSqlSession(
      SqlSessionTemplate.this.sqlSessionFactory,
      SqlSessionTemplate.this.executorType,
      SqlSessionTemplate.this.exceptionTranslator);
  try {
          // 这里触发被拦截的接口调用
          Object result = method.invoke(sqlSession, args);
      if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
        // 成功了之后就触发commit
        sqlSession.commit(true);
      }
      return result;
  } catch (Throwable t) {
    Throwable unwrapped = unwrapThrowable(t);
    if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
      // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
      closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
      sqlSession = null;
      Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
      if (translated != null) {
        unwrapped = translated;
      }
    }
    throw unwrapped;
  } finally {
     if (sqlSession != null) {
     // 下面有源码,详见code--7
       closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
     }
   }
  }
}

2.2.4 code--4

SqlSessionUtils

// 获取SqlSession
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
  notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
  notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
  // 事务锁管理器中获取SqlSessionHolder,如果没有事务的话,这里获取到的就是空的
  SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
  SqlSession session = sessionHolder(executorType, holder);
  if (session != null) {
    return session;
  }
  if (LOGGER.isDebugEnabled()) {
    LOGGER.debug("Creating a new SqlSession");
  }
  // 如果上面没有获取到sqlsession,这里就会去开启一个sqlsseion了
  session = sessionFactory.openSession(executorType);
  // 只有满足这个条件的才会被注册TransactionSynchronizationManager.isSynchronizationActive()
  registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
  return session;
}

2.2.5 code--5

DefaultSqlSession

@Override
public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  // 这里载入参数,封装sql
  BoundSql boundSql = ms.getBoundSql(parameterObject);
  // 这里生成一级缓存的键值,在同一个session中,根据这个键可以从一级缓存中获取到数据而不访问数据库
  CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
  return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

2.2.6 code--6

BaseExecutor

public  List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  // 这里如果配置查询前冲刷缓存,那么就会把一级缓存清理干净,但是这种情况很少
  if (queryStack == 0 && ms.isFlushCacheRequired()) {
    clearLocalCache();
  }
  List list;
  try {
    queryStack++;
    // 从缓存中拿数据
    list = resultHandler == null ? (List) localCache.getObject(key) : null;
    if (list != null) {
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
    } else {
    // 如果缓存中没有这些数据,那么去查询数据库
      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
  } finally {
    queryStack--;
  }
  if (queryStack == 0) {
    for (DeferredLoad deferredLoad : deferredLoads) {
      deferredLoad.load();
    }
    // issue #601
    deferredLoads.clear();
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
      // issue #482
      clearLocalCache();
    }
  }
  return list;
}
// 从数据库中查询数据
private  List queryFromDatabase(MappedStatement ms,
                                      Object parameter,
                                      RowBounds rowBounds,
                                      ResultHandler resultHandler,
                                      CacheKey key,
                                      BoundSql boundSql) throws SQLException {
  List list;
  localCache.putObject(key, EXECUTION_PLACEHOLDER);
  try {
  // 执行查询语句
    list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  } finally {
  // 它这里是先拿出来,在放进去新的,不知道为啥不直接覆盖
    localCache.removeObject(key);
  }
  // 存储新的查询结果
  localCache.putObject(key, list);
  if (ms.getStatementType() == StatementType.CALLABLE) {
    localOutputParameterCache.putObject(key, parameter);
  }
  return list;
}

2.2.7 code--7

SqlSessionUtils

// 关闭session
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
  notNull(session, NO_SQL_SESSION_SPECIFIED);
  notNull(sessionFactory, NO_SQL_SESSION_FACTORY_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();
  }
}
// 处理session的关闭
public void close() {
  try {
    executor.close(isCommitOrRollbackRequired(false));
    closeCursors();
    dirty = false;
  } finally {
    ErrorContext.instance().reset();
  }
}

public void close(boolean forceRollback) {
  try {
    try {
      rollback(forceRollback);
    } finally {
      if (transaction != null) {
      // 事务管理也关闭
        transaction.close();
      }
    }
  } catch (SQLException e) {
    // Ignore.  There's nothing that can be done at this point.
    log.warn("Unexpected exception on closing transaction.  Cause: " + e);
  } finally {
  // 最终主动将所有相关对象引用清空,方便回收,从这里我们就明确的知道了
  // Autowired进来的mapper代理类,是没有一级缓存的功能的,它在查询完毕之后就清理了会话和一级缓存
  // 从这里来看,似乎一级缓存没有帮到什么还拖累了查询
    transaction = null;
    deferredLoads = null;
    localCache = null;
    localOutputParameterCache = null;
    closed = true;
  }
}

你可能感兴趣的:(springboot中mybatis接口调用流程)