深入理解 MyBatis 启动流程(二)

简介: 深入理解 MyBatis 启动流程(二)

执行Map -- maperProxy#


上一个模块我们知道了Mybatis为我们创建出来了mapper接口的代理对象,那当我们获取到这个代理对象之后执行它的mapper.findAll();实际上触发的是代理对象的invoke()方法


所以说,接着看上面的MapperProxyinvoke()方法:


@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);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}


上面的方法我们关注两个地方,第一个地方就是final MapperMethod mapperMethod = cachedMapperMethod(method);,见名知意: 缓存MapperMethod

第一个问题: 这个MapperMethod是什么? 它其实是Mybatis为sql命令+方法全限定名设计的封装类


*/
public class MapperMethod {
  private final SqlCommand command;
  private final MethodSignature method;


说白了,就是想为MapperProxy保存一份map格式的信息,key=方法全名 value=MapperMethod(command,method),存放在MapperProxy的属性methodCache中


comand -> "name=com.changwu.dao.IUserDao,fingAll"

method -> "result= public abstract java.util.List.com.changwu.dao.IUserDao.findAll()"


为什么要给这个MapperProxy保存这里呢? 很简单,它是mapper的代理对象啊,程序员使用的就是他,在这里留一份method的副本,再用的话多方便?


完成缓存后,继续看它如何执行方法:,跟进mapperMethod.execute(sqlSession, args)


因为我使用的是@Select("select * from user"),所以一定进入下面的result = executeForMany(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 SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        ...
}


所以我们继续关注这个 executeForMany(sqlSession, args);方法,看他的第一个参数是sqlSession,也就是我们的DefaultSqlSession,他里面存在两大重要对象: 1是configuration 配置对象, 2是Executor 执行器对象

继续跟进:


private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;  
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
      result = sqlSession.selectList(command.getName(), param);
    }


我们在继续跟进这个selectList方法之前,先看看这个command.getName()是啥? 其实我们上面的代码追踪中有记录: 就是name=com.changwu.dao.IUserDao,fingAll

继续跟进去到下面的方法:


这个方法就比较有趣了,首先来说,下面的入参都是什么


statement = "com.changwu.dao.IUserDao.findAll"parameter=null

第二个问题:MappedStatement是什么? 它是一个对象,维护了很多很多的配置信息,但是我们关心它里面的两条信息,这其实可以理解成一种方法与sql之间的映射,如下图



@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();
    }
  }


前面阅读时,我们知道Mybatis为我们创建的默认的执行器 Executor是CachingExecutor,如下图



继续跟进,主要做了下面三件事, 获取到绑定的sql,然后调用SimpleExecutor缓存key,然后继续执行query()方法


@Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }


接着调用SimpleExecutorquery()方法,然后我们关注SimpleExecutordoQuery()方法,源码如下


@Override
  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.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }


我们注意到.在SimpleExcutor中创建的了一个 XXXStatementHandler这样一个处理器, 所以我们的只管感觉就是,sql真正执行者其实并不是Executor,而是Executor会为每一条sql的执行重新new 一个 StatementHandler ,由这个handler去具体的执行sql

关于这个StatementHandler到底是是何方神圣? 暂时了解它是Mybatis定义的一个规范接口,定义了如下功能即可


public interface StatementHandler {
  // sql预编译, 构建statement对象 
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;
  // 对prepare方法构建的预编译的sql进行参数的设置
  void parameterize(Statement statement)
      throws SQLException;
  // 批量处理器
  void batch(Statement statement)
      throws SQLException;
  // create update delete
  int update(Statement statement)
      throws SQLException;
  // select
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;
  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;
  // 获取sql的封装对象 
  BoundSql getBoundSql();
  // 获取参数处理对象
  ParameterHandler getParameterHandler();
}


了解了这个StatementHandler是什么,下一个问题就是当前我们创建的默认的statement是谁呢? routingStatementHandler如下图



创建preparedStatement#


马上马就发生了一件悄无声息的大事!!!根据现有的sql等信息,构建 PreparedStatement,我们关注这个prepareStatement(handler, ms.getStatementLog());方法,通过调试我们得知,prepareStatement()RoutingStatementHandler的抽象方法,被PreparedStatementHandler重写了,所以我们去看它如何重写的,如下:


@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
  statement = instantiateStatement(connection);


我们关注这个instantiateStatement()方法, 并且进入它的connection.prepareStatement(sql);方法,如下图:



纯洁的微笑... 见到了原生JDK, jdbc的亲人...

创建完事这个 preparedStatement,下一步总该执行了吧...绕这么多圈...

我们回到上面代码中的return handler.query(stmt, resultHandler);准备执行,此时第一个参数就是我们的刚创建出来的PreparedStatement, 回想一下,上面创建的这个默认的statement中的代表是PreparedStatementHandler,所以,我们进入到这个StatementHandler的实现类RountingStatementHandler中,看他的query()方法


@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.query(statement, resultHandler);
  }


调用RountingStatementHandler中维护的代表的StatementHandler也就是PreparedStatementHandlerquery()方法,顺势跟进去,最终会通过反射执行jdbc操作,如图, 我圈出来的对象就是我们上面创建出来的preparedStatement



提交事务#


跟进conmit()方法,分成两步

  • 清空缓存
  • 提交事务

清空缓存是在CachingExecutor中调用了SimpleExecutor简单执行器的方法commit(required)


@Override
  public void commit(boolean required) throws SQLException {
    delegate.commit(required);
    tcm.commit();
  }


接在SimpleExecutor的父类BaseExecutor中完成


@Override
  public void commit(boolean required) throws SQLException {
    if (closed) {
      throw new ExecutorException("Cannot commit, transaction is already closed");
    }
    clearLocalCache();
    flushStatements();
    if (required) {
      transaction.commit();
    }
  }


提交事务的操作在tcm.commit();中完成

相关文章
|
SQL XML Java
MyBatis的动态SQL执行流程
MyBatis的动态SQL执行流程
321 0
|
3月前
|
人工智能 Java 数据库连接
Mybatis执行流程
本文详细分析了 MyBatis 的执行流程,介绍了其核心组件如 SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession 的作用与实现原理,并通过源码解析了 SQL 语句的执行过程,包括动态代理、缓存机制及数据库查询的实现,帮助读者深入理解 MyBatis 的内部工作机制。
100 0
Mybatis执行流程
|
7月前
|
SQL XML Java
一、MyBatis简介:MyBatis历史、MyBatis特性、和其它持久化层技术对比、Mybatis下载依赖包流程
一、MyBatis简介:MyBatis历史、MyBatis特性、和其它持久化层技术对比、Mybatis下载依赖包流程
259 69
|
SQL 存储 缓存
二.吃透Mybatis源码-Mybatis执行流程
上一篇文章我们分析了一下Mybatis的初始化流程,跟踪了一下Mybatis的配置解析过程,SqlSessionFactory和SqlSession的创建过程,这篇文章我们来分析一下SqlSession的执行流程
|
SQL XML Java
MyBatis 动态SQL全流程解析
动态SQL概述 动态SQL是MyBatis 强大功能之一,他免除了在JAVA代码中拼装SQL字符串麻烦,同时保留了我们对SQL的自主控制,更方便进行SQL性能优化改造。 动态SQL中我们使用XML 脚本元素控制SQL的拼装,这都是日常开发中要用到元素,我们一起来回顾一下 if choose (when, otherwise) trim (where, set) foreach if <if test="title != null"> AND title like #{title} </if> 1 2 3 在if元素中通过test接受一个OGNL逻辑表达式,可作常规的逻辑计算如:
388 0
|
Java 关系型数据库 数据库连接
MyBatis 执行流程分析
MyBatis 执行流程分析
|
SQL XML 缓存
mybatis执行流程
mybatis执行流程
219 0
|
SQL XML Java
MyBatis初探:揭示初始化阶段的核心流程与内部机制
MyBatis初探:揭示初始化阶段的核心流程与内部机制
130 2
MyBatis初探:揭示初始化阶段的核心流程与内部机制
|
Java 数据库连接 数据库
MyBatis 核心对象及工作流程?
MyBatis 核心对象及工作流程?
99 0
|
Java 关系型数据库 数据库连接
MyBatis 执行流程分析
MyBatis 执行流程分析
133 2