MyBatis核心组件以及运行原理

概要

想要全面深入的理解mybatis框架,首要前提是学习它的核心组件,我们先来看看MyBatis的“表面现象”——mybatis的核心组件包括:SqlSessionFactoryBuilder(构造器)、SqlSessionFactory(工厂接口)、SqlSession(会话接口)、SQL Mapper(映射器)。通过一张图来整体展示四个核心组件的关系:
MyBatis核心组件以及运行原理_第1张图片

构建SqlSessionFactory过程

使用MyBatis首先是使用配置或者代码去生成SqlSessionFactory,而MyBatis提供了构造器SqlSessionFactoryBuilder,它采用Builder模式来负责构建SqlSessionFactory,通过源码分析,该类下提供了多个build的重载方法。
MyBatis核心组件以及运行原理_第2张图片
但真正重载build方法只有三种:InputStream(字节流)、Reader(字符流)、Configuration(类)。在MyBatis中既可以通过读取XML文件的形式生成SqlSessionFactory,也可以通过Java代码的形式去生成SqlSessionFactory。一般我们常用的是读取XML配置文件的形式。读取XML配置文件的方式构造SqlSessionFactory,构造过程中注入了Configuration的实例对象,之后Configuration实例对象解析XML配置文件来构建SqlSessionFactory:

  SqlSessionFactory sqlSessionFactory = null;
  String resource = "mybatis-config.xml";
        try {
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }

在MyBatis读取XML配置文件后,通过Configuration类对象构建整个MyBatis的上下文,而Configuration采用的是单例模式 (SqlSessionFactory唯一的作用就是生产SqlSession,所以它的责任是唯一的)几乎所有的MyBatis配置都存放在这个单例对象中,以便后续使用。

在MyBayis中SqlSessionFactory是一个接口,它存在两个实现类:和DefaultSqlSessionFactory。SqlSessionManager多使用在多线程环境中。它的具体实现依靠的是DefaultSqlSessionFactory,它们之间的关系如下:
MyBatis核心组件以及运行原理_第3张图片
这种创建方式是一种Builder模式,对于复杂的对象而言,使用构造参数很难实现。这时使用一个类(Configuration)作为统领,一步步地构建所需的内容,然后通过它去创建最终的对象(SqlSessionFactory)。

在构建SqlSessionFactory中,Configuration是最重要的,它的作用是:

  • 读入配置文件。
  • 初始化一些配置。
  • 提工单例,为后续创建SessionFactory服务提供配置参数。
  • 执行一些重要对象的初始化方法。

显然Configuration不是一个简单的类,MyBatis的配置信息都来源于此,Configuration是通过XMLConfigBuilder去构建的,它会读出所有的XML配置信息,把它们解析并保存在Configuration单例中。

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) {
        }
    }
}

当XMLConfigBudiler解析XML时,会将每一个SQL和其配置的内容保存起来,在MyBatis中一条SQL和它相关的配置信息是由3部分组成的,它们分别是MappedStatement、SqlSource和BoundSql

  • MappedStatement MappedStatement维护一条节点的封装
  • SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
  • BoundSql 表示动态生成的SQL语句以及相应的参数信息

通过以上分析,知道了MyBatis会根据文件流生成Configuration对象,进而构建SqlSessionFactory对象得到SqlSession对象了。得到SqlSession对象后就是去执行sql语句。

SqlSession构建

以上我们构建了SqlSessionFactory,在MyBtis中SqlSession是其核心接口,它有两个实现类,DefaultSqlSession和SqlSessionManager。DefaultSqlSession是单线程使用的,而SqlSessionManager在多线程环境下使用。SqlSession的作用类似于JDBC中的Connection对象,代表着一个连接资源的启用,它的作用:

  • 获取Mapper接口。
  • 发送SQL给数据库。
  • 控制数据库事物。

前面我们构建了SqlSessionFactory,有了SqlSessionFactory创建的SqlSession就十分简单了。如下:

SqlSession sqlSession= sqlSessionFactory.openSession();

继续往下看,DefaultSqlSessionFactory实现了openSession()方法,之后调用了openSessionFromDataSource() 方法。

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { 
    Transaction tx = null;
    try {
      //传入的configuration获取环境变量对象、Environment可以配置多个环境配置
      final Environment environment = configuration.getEnvironment();
      //从环境对象中获取事务工厂对象
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //根据DataSource、事务隔离级别、自动提交创建事务对象
      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();
    }
  }

最后调用了new DefaultSqlSession()创建了默认SqlSession。

SqlSession运行过程

SqlSession是一个接口,使用它并不复杂,它给出了查询、插入、更新、删除的方法。但运行过程是MyBatis中最难理解的部分。
先来看看MyBatis的源码是如何实现getMapper方法的。在MyBatis中,通过MapperProxy动态代理Dao, 也就是说当执行自己写的Dao里面的方法的时候,其实是对应的mapperProxy在代理。

通过SqlSession从Configuration中获取

  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }

显然应用到了Configuration对象的getMapper方法来获取对应接口对象,所以继续往下看。

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

它有应用到了映射器的注册器Mapperregistry来获取对应的接口对象。

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

这里首先它判断是否注册一个Mapper,如果没有则会抛出异常信息,如果有就会启用MapperProxyFactory工厂来生成一个代理实例。

@SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    //动态代理我们写的 Dao接口
    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);
  }

在这里可以看到Mapper映射是通过动态代理来实现对接口的绑定的,它的作用就是生成动态代理对象,而代理的方法则被放到了MapperProxy类中。我们再往下看MapperProxy的源码。

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

这里可以看到,如果Mapper是一个JDK动态代理对象,那么它就会运行到invoke方法里面。invoke首先判断是否是一个类,这里Mapper是一个接口不是一个类,所以判定失败。然后生成MapperMethod对象,它是通过cachedMapperMethod方法对其初始化的。最后执行execute方法,把SqlSession和当前运行的参数传递进去。

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

execute()方法主要的作用其实就是先判断CRUD类型,然后根据类型去选择到底执行sqlSession中的哪个方法,代码比较长,我们主要看一下查询最常用的方法executeForMany()方法。

 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.<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;
  }

注意selectList()方法。

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

实际上最后还是回到了SqlSession,然后将CRUD交给了executor()去处理。

至此MyBatis为啥只用Mapper接口就能够运行已经清楚了,因为Mapper的XML文件的命名空间对应的是这个接口的全限定名,而方法就是那条SQL的id。这样MyBatis就可以通过全路径和方法名,将其和代理对象绑定起来。通过动态代理技术,让这个接口运行起来了,而后采用命令模式。最后使用SqlSession接口的方法使得它能够执行对应的SQL。

SqlSession四大对象

映射器就是一个动态代理对进入到了MapperMethod的execute方法,然后它经过判断就进入了SqlSession的delete、update、insert、select等方法。而实际上SqlSession的执行过程是通过Executor、StatementHandler、ParameterHandler和ResultHandler来完成数据库的操作和结果返回的。

Executor:

执行器是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护,在Mybatis中存在三种执行器:

  • SimpleExecutor:简易执行器,默认的执行器。
  • ReuseExecutor:一种执行器重用预处理语句
  • BatchExecutor:这个执行器会批量执行所有更新语句。

在MyBatis中,生成SqlSession的过程中,在DefaultSqlSessionFactory中调用openSession时,又调用了openSessionFromDataSource方法,生成了DefaultSession完成SqlSession的创建的。在openSessionFromDataSource方法中,通过Configuration的newExecutor生成了Executor对象

DefaultSqlSessionFactory中openSessionFromDataSource方法如下:

 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);//生成执行器
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); 
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

Configration的newExecutor方法中根据类型判断创建那种执行器,默认使用的是SimpleExecutor:

public class Configuration {
 
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    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);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
}

以上MyBatis创建了Executor,那么它是如何去处理CRUD操作的呢?之前的executeForMany()方法中调用了selectList()方法,而在selectList()方法中,源码如下:

 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) {
      ......
    }
  }

executor.query()通过一层层的调用最后来到了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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      //StatementHandler封装了Statement, 让 StatementHandler 去处理
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

prepareStatement方法:

 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

显然MyBatis根据Configeration来构建了StatementHandler,然后使用prepareStatement方法,对SQL编译和参数进行初始化,这样焦点又转移到了StatementHandler对象上。

StatementHandler:

数据库会话器封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数等

在MyBatis生成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;
  }

显然创建的真实对象是RoutingStatementHandler的对象,它实现了接口StatementHandler 。RoutingStatementHandler并不是真正的服务对象,而是通过适配器模式找到对应的StatementHandler来执行的。和Executor一样,mybatis提供了三种会话处理器:

  • CallableStatementHandler:对应JDBC里面的CallableStatement(存储过程处理)类。
  • PreparedStatementHandler:对应JDBC里面的PreparedStatement(预编译处理)类。
  • SimpleStatementHandler:对应JDBC里面的Statement类。

在初始化RoutingStatementHandler对象时,它会根据上下文环境来决定创建哪个具体的StatementHandler实例,源码如下:

public class RoutingStatementHandler implements StatementHandler {

  private final StatementHandler delegate;

  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;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

它定义了一个对象的适配器——delegate,然后构造方法根据配置来适配对应的StatementHandler对象,三种Handler并没有直接实现StatementHandler,而是继承了BaseStatementHandler,BaseStatementHandler中实现了StatementHandler接口,默认情况下是用PreparedStatementHandler(在MappedStatement的Builder方法里设置)

Executor执行查询时会执行StatementHandler的prepare、parameterize和query方法,其中PreparedStatementHandler的prepare方法代码如下:

public abstract class BaseStatementHandler implements StatementHandler {
......

 @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      //这里初始化JDBC的Statement对象,instantiateStatement()需要由 BaseStatementHandler的子类实
      statement = instantiateStatement(connection);
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }
}

instantiateStatement()方法是对SQL进行了预编译,然后做一些基础配置,比如超时、获取的最大行数等。Executor中会调用它的parameterize()方法去设置参数,如下:

 @Override
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

这个时候它就调用了PreparedStatement去完成,此时我们先看一下查询的方法——SimpleExecutor中的doQuery()方法中调用的query()方法执行SQL返回结果,如下:

 @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }

在执行前参数和SQL都被prepare()方法预编译了,参数在parameterize()方法中已经进行了设置,所以只要执行SQL,然后返回结果集就可以了。执行后可以看到ResultSetHandler对结果集的封装和返回。

ParameterHandler:

负责对用户传递的参数转换成JDBC Statement 所对应的数据类型,完成对预编译参数得设置。

ParameterHandler只是一个接口,它有一个实现类DefaultParameterHandler

public interface ParameterHandler {
 
  Object getParameterObject();
 
  void setParameters(PreparedStatement ps)
      throws SQLException;
 
}

DefaultParameterHandler的setParameters方法中,可以看到从parameterMapping中拿到了typeHandler类型处理器,然后设置参数。

public class DefaultParameterHandler implements ParameterHandler {

  private final TypeHandlerRegistry typeHandlerRegistry;

  private final MappedStatement mappedStatement;
  private final Object parameterObject;
  private final BoundSql boundSql;
  private final Configuration configuration;

  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }

  @Override
  public Object getParameterObject() {
    return parameterObject;
  }

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

}

TypeHandler用于实现JAVA类型和JDBC类型的相互转换,它会根据参数的JAVA类型和JDBC类型选择合适的TypeHandler,再通过TypeHandler进行参数设置,以此达到JAVA类型到JDBC类型的转换。而TypeHandler也是在MyBatis初始化时,注册到Configuration里面的,需要时就可以直接拿过来用了,MyBtais就是通过这样完成参数设置的。

ResultSetHandler:

负责将JDBC返回的ResultSet结果集对象转换成List类型的集合

回到PreparementStatementHandler类,以query方法为例,方法调用了resultSetHandler. handleCursorResultSets(ps)方法返回结果集

public class PreparedStatementHandler extends BaseStatementHandler {

  public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

  @Override
  public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    int rows = ps.getUpdateCount();
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
  }

  @Override
  public void batch(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.addBatch();
  }

   //对结果集处理
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }

  @Override
  public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleCursorResultSets(ps);
  }
  .....
 }

resultSetHandler变量的定义在BaseStatementHandler中:

public abstract class BaseStatementHandler implements StatementHandler {
 
  protected final Configuration configuration;
  protected final ObjectFactory objectFactory;
  protected final TypeHandlerRegistry typeHandlerRegistry;
  protected final ResultSetHandler resultSetHandler;//结果集处理器
  protected final ParameterHandler parameterHandler;//参数处理器
 
  protected final Executor executor;
  protected final MappedStatement mappedStatement;
  protected final RowBounds rowBounds;
...
}

ResultSetHandler也只是一个接口,handleResultSets()方法,它是用来包装结果集的,在它的子类DefaultResultSetHandler中实现:

public interface ResultSetHandler {
 
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;
 
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
 
  void handleOutputParameters(CallableStatement cs) throws SQLException;
 
}
 @Override
  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;
    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);
  }

总结

SqlSession是通过执行器Executor调度StatementHandler来运行的。而StatementHandler经过3步:

  • prepared预编译SQL
  • parameterize设置参数
  • query/update执行SQL

其中,parameterize是调用parameterHandler的方法设置的,而参数是根据类型处理器TypeHandler处理的。query/update方法通过ResultSetHandler进行处理结果的封装,如果是update语句,就返回参数,否则就通过TypeHandler处理结果类型,然后用ObjectFactory提供的规则组装对象,返回调用者。这便是SqlSession执行的过程。
MyBatis核心组件以及运行原理_第4张图片

参考:
https://blog.csdn.net/lom9357bye/article/details/80210661 ——【Mybatis源码】SqlSession四大对象
https://www.cnblogs.com/shamo89/p/9957291.html ——Mybatis(四):MyBatis核心组件介绍原理解析和源码解读
https://blog.csdn.net/liming363873117/article/details/60467107 ——mybatis框架(二)——核心组件及其作用
《JavaEE 互联网轻量级框架整合开发》SSM框架和Redis实现

你可能感兴趣的:(SSM原理整合)