美文网首页
Mybatis源码分析

Mybatis源码分析

作者: Zeppelin421 | 来源:发表于2021-08-07 23:15 被阅读0次

架构设计

  • API层
    提供给外部使用的接口API,开发人员通过这些本地API来操控数据库
    与数据库交互有两种方式:使用传统的MyBatis提供的API;使用Mapper代理;
  • 数据处理层
    负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理。它主要的目的是根据调用的请求完成一次数据库操作
  • 基础支撑层
    负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件,为上层的数据处理层提供最基础的支撑

主要构件及其相互关系

  • SqlSession
    作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能
  • Executor
    MyBatis执行器,是MyBatis调度的核心,负责SQL语句的生成和查询缓存的维护
  • StatementHandler
    封装了JDBC Statement操作,负责对JDBC statement的操作,如设置参数、将statement结果集转换成List集合
  • ParameterHandler
    负责对用户传递的参数转换成JDBC statement所需要的参数
  • ResultSetHandler
    负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
  • TypeHandler
    负责Java数据类型和jdbc数据类型之间的映射和转换
  • MappedStatement
    MappedStatement维护了一条<select|update|delete|insert>节点的封装
  • SqlSource
    负责根据用户传递的parameterObject动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
  • BoundSql
    表示动态生成的SQL语句以及相应的参数信息
  • Configuration
    MyBatis所有的配置信息都维持在Configuration对象之中

传统方式源码剖析

初始化
  InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
  SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);

SqlSessionFactory构建器将数据流交给XMLConfigBuilder来解析,并将解析出来的数据存储在Configuration对象中,最后利用Configuration构建出SqlSessionFactory对象

  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      // XMLConfigBuilder是专门解析MyBatis的配置文件的类
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // parser.parse()返回值是Configuration对象
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

XmlConfig构建器从数据流中解析出properties、settings、typAliases、typeHandlers、objectFactory、mappers等数据,存储在Configuration实例中

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return 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);
    }
  }
执行SQL流程
  SqlSession sqlSession = sqlSessionFactory.openSession();
  List<User> users = sqlSession.selectList("com.hooda.test.dao.UserMapper.selectList");

SqlSession

SqlSession是一个接口,有两个实现类:DefaultSqlSession和SqlSessionManager(弃用)
SqlSession是MyBatis中用于和数据库交互的顶层类,通常将它和ThreadLocal绑定,一个会话使用一个SqlSession并且在使用完后需要close

public class DefaultSqlSession implements SqlSession {
  private final Configuration configuration;
  private final Executor executor;
}

SqlSession中有两个最重要的参数,Configuration与初始化相同,Executor为执行器

Executor

Executor也是一个接口,有三个常用的实现类:
BatchExecutor:重用语句并执行批量更新
ReuseExector:重用预处理语句preparedStatement
SimpleExecutor:普通执行器

获取SqlSession

  public SqlSession openSession() {
    // Configuration中 defaultExecutorType = ExecutorType.SIMPLE;
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  // ExecutorType 为Executor的类型
  // TransactionIsolationLevel为事务隔离级别
  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);
      // 根据参数创建指定类型的Executor
      final Executor executor = configuration.newExecutor(tx, execType);
      // 返回的是 DefaultSqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

执行SqlSession中的API

  public <E> List<E> selectList(String statement) {
    return this.selectList(statement, null);
  }

  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      // 根据传入的全限定名+方法名从映射map中取出MappedStatement对象
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 调用Executor中的方法处理
      // RowBounds是用来逻辑分页
      // wrapCollection(parameter)是用来装饰集合或者数组参数
      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();
    }
  }

Executor源码

  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, 
        ResultHandler resultHandler) throws SQLException {
    // 根据传入的参数动态获取SQL语句,最后返回用BoundSql对象表示
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 本次查询创建缓存key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }

  public <E> List<E> 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<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) 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 <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, 
        ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> 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;
  }
  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对象来执行查询
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, 
          rowBounds, resultHandler, boundSql);
      // 创建JDBC中的Statement对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      // StatementHandler进行处理
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 代码中的getConnection方法经过重重调用最后会调用openConnection方法,从连接池中获得链接
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }
  protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
      log.debug("Opening JDBC Connection");
    }
    // 从连接池中获取链接
    this.connection = this.dataSource.getConnection();
    if (this.level != null) {
      this.connection.setTransactionIsolation(this.level.getLevel());
    }
  }

Executor的功能和作用
1> 根据传递的参数完成SQL语句的动态解析,生成BoundSql对象,共StatementHandler使用
2> 为查询创建缓存,以提高性能
3> 创建JDBC的Statement连接对象,传递给StatementHandler对象,返回List查询结果

StatementHandler源码

  // 调用preparedStatement.execute方法,然后将ResultSet交给ResultSetHandler处理
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }
 
  // 使用ParameterHandler对象来完成对Statement的设值
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

StatementHandler主要工作
1、对于JDBC的PreparedStatement类型的对象,创建过程中,我们使用的是SQL语句字符串会包含若干个?占位符,我们其后再对占位符进行设值。StatementHandler通过parameter(statement)方法对Statement进行设值
2、StatementHandler通过query方法来完成Statement和将Statement对象返回的ResultSet封装

ParameterHandler源码

  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          // 每一个Mapping都有一个TypeHandler,根据TypeHandler来对preparedStatement进行设置参数
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            // 设置参数
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException e) {
            throw new TypeException("Could not set parameters for mapping: "+parameterMapping+". Cause: " + e, e);
          } catch (SQLException e) {
            throw new TypeException("Could not set parameters for mapping: "+parameterMapping+". Cause: " + e, e);
          }
        }
      }
    }
  }

ResultSetHandler源码

  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
    // 多ResultSet的结果集合,每个ResultSet对应一个Object对象。实际上,每个Object是List<Object>对象
    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    // 获取首个ResultSet对象,并封装成ResultSetWrapper对象
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    // 在不考虑存储过程的多ResultSet的情况,普通的查询实际上就一个ResultSet,也就是说ResultMaps就一个元素
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      // 获得ResultMap对象
      ResultMap resultMap = resultMaps.get(resultSetCount);
      // 处理ResultSet,将结果添加到MultipleResults中
      handleResultSet(rsw, resultMap, multipleResults, null);
      // 获得下一个ResultSet对象,并封装成ResultSetWrapper对象
      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++;
      }
    }
    // 如果是MultipleResults单元素,则取首元素返回
    return collapseSingleResultList(multipleResults);
  }

相关文章

网友评论

      本文标题:Mybatis源码分析

      本文链接:https://www.haomeiwen.com/subject/qtwjvltx.html