一文搞懂Mybatis原理

文章目录


前言:
当我们初步了解了mybatis的快速入门quick start, 那么我们就该思考 ,
咱们平常写的mapper是接口的形式,是如何映射到具体的SQL上的?
mybatis的一级缓存session,二级缓存application是什么?
通过plugin我们又可以对mybatis做哪些操作或者定制化,又有哪些实现?

一.快速入门

//(1)读取mybatis配置文件
String resource = "config/mybatis-config.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(resource);
//(2)Builder模式建造工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//(3)获取session一个会话
try(SqlSession session =sqlSessionFactory.openSession()) {
    //(4)获取对应的mapper
    UserMapper mapper = session.getMapper(UserMapper.class);
    List<User> users = mapper.selectAllUser();
    List<User> users1 = mapper.selectById(1);
}

二.查询流程分析

2.1首先通过ClassLoader读取配置文件生成输入流

2.2建造者模式加载配置创建SQLSessionFactory

2.2.1SQLSessionFactoryBuilder.builder
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    //通过XMLConfigBuilder器解析配置文件  
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    .....
  }
}
2.2.2生成的parser.parse()去解析xml配置文件 , 解析出mybatis的核心之一Configuration
public Configuration parse() {
    //是否已经解析过了
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  //开始解析节点configuration
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

2.2.3parseConfiguration会将xml中的所有配置解析进入Configuration中
private void parseConfiguration(XNode root) {
  try {
    //issue #117 read properties first
    propertiesElement(root.evalNode("properties"));
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(settings);
    typeAliasesElement(root.evalNode("typeAliases"));
    pluginElement(root.evalNode("plugins"));
    objectFactoryElement(root.evalNode("objectFactory"));
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    settingsElement(settings);
    // read it after objectFactory and objectWrapperFactory issue #631
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}
2.2.4解析完成后SQLSessionFactoryBuilder.build 会new DefaultSQLSessionFactory, 实现了SQLSessionFactory接口, 典型的模板模式
public SqlSessionFactory build(Configuration config) {
    //传入Configuration
  return new DefaultSqlSessionFactory(config);
}

3.3在完成创建一个SQLSessionFactory之后,我们可以去开启一个SqlSession

3.3.1DefaultSQLSessionFactory开启一个Sqlsession
public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}


private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
      //读取环境
    final Environment environment = configuration.getEnvironment();
    //获取事务管理工程
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    //创建一个执行器
    final Executor executor = configuration.newExecutor(tx, execType);
    //创建一个DefaultSqlSession
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } 
  ......省略
}
3.3.2创建一个Executor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    //默认的SimpleExecutor
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  //策略模式 + 工厂模式
  if (ExecutorType.BATCH == executorType) {
    executor = new BatchExecutor(this, transaction);
  } else if (ExecutorType.REUSE == executorType) {
    executor = new ReuseExecutor(this, transaction);
  } else {
    executor = new SimpleExecutor(this, transaction);
  }
  //二级缓存开启的地方 
  if (cacheEnabled) {
    //这里是装饰者模式 对方法的增强与扩展
    executor = new CachingExecutor(executor);
  }
  //mybatis核心之一 plugin插件, 可通过该功能实现拦截 sql上报监控等功能 pagehelper就是借用此实现
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

3.3在创建SqlSession后我们就可以获取Mapper,调用具体的接口方法了

3.3.1DefaultSqlSession.getMapper从Configuration中读取
public <T> T getMapper(Class<T> type) {
  return configuration.<T>getMapper(type, this);
}
3.3.2从MapperRegistry.knownMappers中读取加载到的MapperProxyFactory
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
      //去创建代理对象
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}
3.3.3通过jdk动态代理生成代理MapperProxy代理对象
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}

4.调用mapper查询.getUserById或者其他方法 实际上是调用MapperProxy.invoke方法

4.4.1调用MapperProxy.invoke方法
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);
  }
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}
4.4.2从缓存中获取MapperMethod
private MapperMethod cachedMapperMethod(Method method) {
  MapperMethod mapperMethod = methodCache.get(method);
  if (mapperMethod == null) {
      //创建一个新的MapperMethod
    mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
    methodCache.put(method, mapperMethod);
  }
  return mapperMethod;
}
4.4.3创建MapperMethod
目的是在invoke的时候真正维护变化信息的对象, 如方法的类型, sql等
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    //构建SqlCommand
  this.command = new SqlCommand(config, mapperInterface, method);
  //构建MethodSignature
  this.method = new MethodSignature(config, mapperInterface, method);
}
4.4.4根据MapperMethod.command策略区分执行不同的方法类型
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 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 {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
      break;
    .....省略
    default:
      throw new BindingException("Unknown execution method for: " + command.getName());
  }
  ....省略
  return result;
}
4.4.5我们调用查询结构调用executeForMany
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
  List<E> result;
  //根据@param等解析方法入参
  Object param = method.convertArgsToSqlCommandParam(args);
  //是否使用了mybatis提供的rowbounds
  if (method.hasRowBounds()) {
    RowBounds rowBounds = method.extractRowBounds(args);
    result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
  } else {
    result = sqlSession.<E>selectList(command.getName(), param);
  }
  // issue #510 Collections & arrays support
  if (!method.getReturnType().isAssignableFrom(result.getClass())) {
    if (method.getReturnType().isArray()) {
      return convertToArray(result);
    } else {
      return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
    }
  }
  return result;
}
4.4.6执行sqlSession.selectList方法
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
      //从Configuration获取mapper全路径和与之对应mapper
    MappedStatement ms = configuration.getMappedStatement(statement);
    //Executor去执行真正sql操作
    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();
  }
}
4.4.7Executor.query查询sql
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
      //获取与之绑定的sql
    BoundSql boundSql = ms.getBoundSql(parameter);
    //一级缓存 创建CacheKey
   CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
   return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

继续看query重载方法, 我们查看CachedExecutor

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
  //从MappedStatment获取cache, ms是维护在Configuration中的所以二级缓存是application级别的      
  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;
    }
  }
  //委派真正的Executor.query
  return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

BaseExecutor.query 模板模式执行

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  ....省略
  List<E> list;
  try {
    queryStack++;
    //如果没有resultHandler处理器 那么直接从缓存中返回数据, 如果此时有标记则异常失败
    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
    if (list != null) {
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
    } else {
        //如果有resultHandler或者缓存没数据
      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
  } finally {
    queryStack--;
  }
  .....省略
  return list;
}
4.4.8如果缓存为null或者设置了ResultHandler我们需要查询数据库queryFromDatabase
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  List<E> list;
  //对localCache增加个查询标记
  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;
}
4.4.9调用SimpleExecutor.doQuery方法
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    //创建新的StatementHandler可被plugin处理
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.<E>query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}
4.4.10创建StatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  switch (ms.getStatementType()) {
    case STATEMENT:
      delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case PREPARED:
      delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    ....
        }
}
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  ......
  if (boundSql == null) { // issue #435, get the key before calculating the statement
    generateKeys(parameterObject);
    boundSql = mappedStatement.getBoundSql(parameterObject);
  }

  this.boundSql = boundSql;
  //配置的paramterHandler和ResultSetHandler   可以被plugin处理
  this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
  this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
4.4.11调用prepareStatement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  //获取数据库Connection
  Connection connection = getConnection(statementLog);
  //对connection预处理
  stmt = handler.prepare(connection, transaction.getTimeout());
  //parameterHandler处理
  handler.parameterize(stmt);
  return stmt;
}
4.4.12StatementHandler.query执行下面查询操作
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  //查询
  ps.execute();
  //交由resultSetHandler处理结果
  return resultSetHandler.<E> handleResultSets(ps);
}public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  return resultSetHandler.<E> handleResultSets(ps);
}
4.4.13ResultSetHandler对结果进行下一步处理
public List<Object> handleResultSets(Statement stmt) throws SQLException {
  ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
  final List<Object> multipleResults = new ArrayList<Object>();
  int resultSetCount = 0;
  //对ResultSet进行下一步包装
  ResultSetWrapper rsw = getFirstResultSet(stmt);
  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  int resultMapCount = resultMaps.size();
  validateResultMapsCount(rsw, resultMapCount);
  while (rsw != null && resultMapCount > resultSetCount) {
    ResultMap resultMap = resultMaps.get(resultSetCount);
    handleResultSet(rsw, resultMap, multipleResults, null);
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
  }
  String[] resultSets = mappedStatement.getResultSets();
  if (resultSets != null) {
    while (rsw != null && resultSetCount < resultSets.length) {
      ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
      if (parentMapping != null) {
        String nestedResultMapId = parentMapping.getNestedResultMapId();
        ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
        handleResultSet(rsw, resultMap, null, parentMapping);
      }
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
  }
  return collapseSingleResultList(multipleResults);
}
4.4.14handlerResultSet对结果处理
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
  try {
    if (parentMapping != null) {
        //处理分页情况
      handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
    } else {
      if (resultHandler == null) {
        DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
        handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
        multipleResults.add(defaultResultHandler.getResultList());
      } else {
        handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
      }
    }
  } 
  ......
}
//逻辑分页
private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
  final DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
  skipRows(rsw.getResultSet(), rowBounds);
  Object rowValue = previousRowValue;
  while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
    final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
    final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
    Object partialObject = nestedResultObjects.get(rowKey);
    // issue #577 && #542
    .....
      }
  .....
}

三.问题解答

3.1mybatis怎么解决的mapper接口无具体实现的

答:通过jdk动态代理,通过维护MapperProxy中维护的MapperMethod来控制处理

3.2plugin的局限性

只能处理ParamHandler ResultSetHandler StatementHandler Executor

3.3mybatis的log问题

按顺序初始化日志在LogFactory中
在这里插入图片描述

3.4一级缓存和二级缓存

一级缓存是在Executor中的维护的loaclCache的map结构, Executor是随着创建SqlSession一同创建的所以级别是Session级别
二级缓存是在MappedStatement中维护的因为MappedStatement是在框架启动时跟着维护在Configuration中的所以是Application级别的

四.mybatis的结构图和流程图

在这里插入图片描述
在这里插入图片描述

发布了235 篇原创文章 · 获赞 221 · 访问量 96万+

猜你喜欢

转载自blog.csdn.net/drdongshiye/article/details/104872186