MyBatis执行流程源码分析

MyBatis执行流程源码分析

  • 1. MyBatis概述
  • 2. MyBatis执行流程分析
    • 2.1 MyBatis执行流程说明
    • 2.2 MyBatis执行流程图
  • 3.MyBatis传统模式源码分析
    • 3.1 传统模式执行流程
    • 3.2 读取Mybatis配置文件
    • 3.3 创建SqlSessionFactory
    • 3.4 创建SqlSession
    • 3.5 sqlSession执行查询
  • 4. MyBatis接口模式源码分析
    • 4.1 接口模式执行流程
    • 4.2 读取Mybatis配置文件
    • 4.3 创建SqlSessionFactory
    • 4.4 创建SqlSession
    • 3.5 通过SqlSession创建Mapper接口的代理对象
    • 3.6 使用代理对象执行方法
  • 5. 参考文档

1. MyBatis概述

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

测试源码地址:https://gitee.com/rjzhu/opencode/tree/master/mybatis-cache-demo-master

2. MyBatis执行流程分析

2.1 MyBatis执行流程说明

1.MyBatis配置文件,MybatisConfig.xml,Mapper.xml
2.SqlSessionFactoryBuilder,加载配置文件,构建Configuration对象,根据Configuration对象创建SqlSessionFactory
3.SqlSessionFactory,调用openSession方法,创建SqlSession对象
4.SqlSession,根据事物Transaction与Executor类型创建Executor
5.Executor,通过Executor的doQuery创建StatementHandler对象
5.Executor,通过Executor的doQuery创建StatementHandler对象
7.ParameterHandler,对于处理后的statement参数绑定
8.执行sql,通过statement.execute(sql)执行sql
9,ResultHandler,通过ResultHandler结果集封装

2.2 MyBatis执行流程图

MyBatis执行流程源码分析_第1张图片

3.MyBatis传统模式源码分析

3.1 传统模式执行流程

/**
 * mybatis执行流程:传统模式
 */
@Test
public void myBatisRaditionModel() throws Exception {
    // 1.读取Mybatis配置文件
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

    // 2.通过sqlSessionFactoryBuilder 构建 SqlSessionFactory
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

    // 3.通过sqlSessionFactory开启SqlSession
    SqlSession sqlSession = factory.openSession(true);

    // 4.使用代理对象执行方法,--与接口模式的区别
    List<StudentEntity> list = sqlSession.selectList("mapper.StudentMapper.getStudentById", 1);
    System.out.println("查询成功:" + list);

    // 6.释放资源
    sqlSession.close();
    in.close();
}

3.2 读取Mybatis配置文件

将配置文件读取流的形式
InputStream in = Resources.getResourceAsStream(“mybatis-config.xml”);

3.3 创建SqlSessionFactory

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
    //构建XMLConfigBuilder对象,解析xml
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //创建SqlSessionFactory 
      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.
      }
    }
  }
  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

  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);
    }
  }
  //解析构建SqlSessionFactory 
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      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);
  }

3.4 创建SqlSession

  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);
      //创建SqlSession 
      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();
    }
  }

3.5 sqlSession执行查询

// 4.使用代理对象执行方法
List<StudentEntity> list = sqlSession.selectList("mapper.StudentMapper.getStudentById", 1);

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
    //创建MappedStatement 
      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();
    }
  }
  
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  //创建BoundSql 
    BoundSql boundSql = ms.getBoundSql(parameter);
    //创建缓存
    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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    //具体执行查询sql
    statement.execute(sql);
    return resultSetHandler.<E>handleResultSets(statement);
  }

4. MyBatis接口模式源码分析

4.1 接口模式执行流程

/**
 * mybatis执行流程:接口模式
 */
@Test
public void myBatisInterfaceModel() throws Exception {
    // 1.读取Mybatis配置文件
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

    // 2.通过sqlSessionFactoryBuilder 构建 SqlSessionFactory
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

    // 3.通过sqlSessionFactory开启SqlSession
    SqlSession sqlSession = factory.openSession(true);

    // 4.通过SqlSession创建Mapper接口的代理对象,--与接口模式的区别
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

    // 5.使用代理对象执行方法
    StudentEntity studentEntity = studentMapper.getStudentById(1);
    System.out.println(studentEntity);

    // 6.释放资源
    sqlSession.close();
    in.close();
}

4.2 读取Mybatis配置文件

同上
将配置文件读取流的形式
InputStream in = Resources.getResourceAsStream(“mybatis-config.xml”);

4.3 创建SqlSessionFactory

同上

4.4 创建SqlSession

同上

3.5 通过SqlSession创建Mapper接口的代理对象

// 4.通过SqlSession创建Mapper接口的代理对象
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

	//这里返回的是mapper接口的代理对象
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

3.6 使用代理对象执行方法

//使用代理对象执行方法
StudentEntity studentEntity = studentMapper.getStudentById(1);

//通过代理对象调用执行方法封装结果集,通过MapperProxy执行invoke方法
  @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 {
        // 调用反射方法
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
  
    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
    // 调用执行器
      return mapperMethod.execute(sqlSession, args);
    }


  /**
   * 通过标签判断执行类型,封装结果集
   */
  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 {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        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;
  }


5. 参考文档

MyBatis中文网:https://mybatis.net.cn/index.html
MyBatis英文网:https://mybatis.org/mybatis-3/index.html

MyBatis工作原理及流程详细讲解:https://baijiahao.baidu.com/s?id=1666562082269776543&wfr=spider&for=pc

mybatis框架的架构(图解):https://www.cnblogs.com/wyhluckdog/p/10147921.html

MyBatis执行流程的各阶段介绍:https://www.cnblogs.com/-beyond/p/13232624.html

你可能感兴趣的:(基础框架,MyBatis执行流程源码分析)