头图

概要

mybatis是国内最流行的轻量级的持久层的框架,帮我们屏蔽了繁琐重复的JDBC操作,并且通过映射配置文件或相应注解将ResultSet映射为Java对象,并且支持嵌套,完成复杂的sql查询和返回类型转换

与hibernate等持久层框架相比,小巧灵活,可插拔式的插件、灵活的sql,给优化数据库层提供了良好的方便,本文将对其中的组件给与介绍,给出这些组件的源码分析

一个简单的mybatis应用项目结构

先看一下github上的一个mybatis的简单项目,仓库链接如下
https://github.com/tangbu/myblob/tree/master/java/mybatis-learning
我们梳理一下使用mybatis需要进行的基本配置

  1. 核心配置文件mybatis-config.xml
  2. 实体类UserEntity
  3. 操作实体类的UserMapper接口
  4. 承载UserMapper接口sql实现的UserMapper.xml文件

看一下mybatis启动方法

@Test
public  void testMybatis () throws IOException {
     InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
     SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
     SqlSession sqlSession = sqlSessionFactory.openSession();
     UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
     List<UserEntity> userList = userMapper.listAllUser();
        System.out.println(userList);
    }

执行以上测试函数,打印出了数据库中的数据,由此,mybatis帮我们封装的jdbc实现已经调用完成,

核心配置文件

贴一下mybatis的核心配置文件mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 配置全局属性 -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

    <typeAliases>
        <package name="org.learning.entity"/>
    </typeAliases>

    <!-- 类型处理器:定义Java类型与数据库中的数据类型之间的转换关系 -->
    <typeHandlers>
        
    </typeHandlers>

    <!-- 5、对象工厂  默认是DefaultObjectFactory的实现-->
    <objectFactory type=""></objectFactory>

    <!-- 6、插件:mybatis的插件,支持自定义插件 比如PageHelper插件-->
    <plugins>
        <plugin interceptor=""></plugin>
    </plugins>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC">
                <property name="" value=""/>
            </transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="org.hsqldb.jdbcDriver"/>
                <property name="url" value="jdbc:hsqldb:mem:mybatis"/>
                <!--填写你的数据库用户名-->
                <property name="username" value="sa"/>
                <!--填写你的数据库密码-->
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <package name="org.learning.mapper"/>
    </mappers>
</configuration>

各组件的基本介绍

看下configuration源码中组件,对应着mybatis-config.xml来看

// xml中的根节点即为configuration节点
public class Configuration {

  protected Environment environment;

/* 下面内容解析为<setting>.....</setting>节点
  protected boolean safeRowBoundsEnabled;
  protected boolean safeResultHandlerEnabled = true;
  protected boolean mapUnderscoreToCamelCase;
  protected boolean aggressiveLazyLoading;
 ............*/

  protected Properties variables = new Properties();
  // <reflectorFactory/> 标签
  protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
  // <objectFactory/> 标签
  protected ObjectFactory objectFactory = new DefaultObjectFactory();
  // <objectWrapperFactory/>
  protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
  protected boolean lazyLoadingEnabled = false;
  protected ProxyFactory proxyFactory = new JavassistProxyFactory();
  protected String databaseId;

  protected Class<?> configurationFactory;
// <mappers/>标签
  protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
  // <plugins/>标签
  protected final InterceptorChain interceptorChain = new InterceptorChain();
  // <typeHandlers>标签
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
  // <typeAliases/>标签
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

//  mapper.xml或注解中的每一个sql段的信息
  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
  protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
  //  ResultMap标签对象
  protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
  //  ParameterMap标签对象
  protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
  protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");

  protected final Set<String> loadedResources = new HashSet<String>();
  protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

  // 存放解析异常的XMLStatementBuilder对象
  protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
  // 存放解析异常的CacheRefResolver对象
  protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
  // 存放解析异常的ResultMapResolver对象
  protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
  // 存放解析异常的MethodResolver对象
  protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
  }

从中,我们可以看到Configuration对象映射出配置文件的很多组件,其他组件在需要的时候也会从Configuration对象中拿基本的配置信息。

重点看一下如下几个组件:

  1. mapperRegistry 映射mapper接口类和MapperProxyFactory对象,sqlSession.getMapper(UserMapper.class)获取UserMapper的时候,由MapperProxyFactory对象基于UserMapper.xml配置生成代理实现
  2. InterceptorChain,各种插件如分页插件,在mybatis的执行流程中以拦截器的形式出现
  3. TypeHandlerRegistry 处理映射数据库字段和java字段关系的表对象
  4. mappedStatement 处理单个语句的映射,一条select、update语句,还会包含入参类型、出参类型、sql片段等
  5. ResultMaps、parameterMaps 建立数据库字段和实体映射关系,id为该片段的命名空间加id,value即为对应标签解析后生成的对象

此外,在mybatis启动方法中,我们还接触到了

  1. SqlSessionFactoryBuilder对象 ,解析完xml中Configuration后传递给SqlSessionFactory
  2. SqlSessionFactory SqlSession的工厂
  3. SqlSession接口,定义了ibatis中的crud操作,mybatis后的MapperProxy代理对象执行sql操作最后也会到SqlSession的默认实现DefaultSqlSession来进行操作
  4. MapperProxyFactory 创建Mapper代理对象的工厂
  5. Mapper的代理对象MapperProxy
  6. MapperMethod, 持有sql的crud类型、返回类型,游标,就是当前执行sql的mapper中的方法映射
  7. Executor真正执行数据库操作执行器

这些组件的灵活组装完成的我们使用mybatis的强大封装。接下来我们结合程序运行的代码逻辑来看一下这些组件是如何协同工作的。

Configuration对象

Configuration基本功能

分析源码我们可以得出configuration的三个主要方法

  • 存储mybatis标签中settings标签中的配置信息和基本实现

    <settings>
          <setting name="logImpl" value="LOG4J"/>
      </settings>
      
     ....
     protected Class <? extends Log> logImpl;
  • 支持各种组件的注册和存储 比如mapperRegistry、typeHandlerRegistry等

    // 成员变量...
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    protected final InterceptorChain interceptorChain = new InterceptorChain();
    protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
    
     // 成员方法...
     public void addInterceptor(Interceptor interceptor) {
      interceptorChain.addInterceptor(interceptor);
    }
    
    public void addMappers(String packageName, Class<?> superType) {
      mapperRegistry.addMappers(packageName, superType);
    }
  • 提供一些组件生成的工厂方法

    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
      .......
      return parameterHandler;
    }
    
    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
        ResultHandler resultHandler, BoundSql boundSql) {
      ......
    }
    
    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    ........
      return statementHandler;
    }

    Configuration xml的解析

    在SqlSessionFactoryBuilder()创建build方法生成SqlSessionFactory的时候,parser.parse()生成了Configuration对象

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
      try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        return build(parser.parse());
      } 
      ...

    看下XMLConfigBuilder是如何解析xml配置文件的

    private void parseConfiguration(XNode root) {
      try {
        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"));
      }
    }

    mapper注册进Configuration案例

    进入mapperElement(root.evalNode("mappers"));的方法里看一下节点解析

    private void mapperElement(XNode parent) throws Exception {
      if (parent != null) {
        for (XNode child : parent.getChildren()) {
        // 调用configuration的添加整个package的方法
          if ("package".equals(child.getName())) {
            String mapperPackage = child.getStringAttribute("name");
            configuration.addMappers(mapperPackage);
          } else {
            // 注册单个mapper....
          }
        }
      }
    }

    SqlSession对象

    SqlSession基本功能

    拿到数据库连接后,sqlSession接口是mybatis直接与数据库交互的接口,主要看默认实现DefaultSqlSession,可以直接查询数据库

    public interface SqlSession extends Closeable {
    <T> T selectOne(String statement);
    <T> T selectOne(String statement, Object parameter);
    <E> List<E> selectList(String statement);
    ...
    }

    SqlSession的创建源码

    SqlSession sqlSession = sqlSessionFactory.openSession();

    通过默认实现便走到了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);
      } 
      .....

    DefaultSqlSession的实现

    sqlSession可以执行数据库的crud操作,找个例子看,可以发现,最终都是由executor进行的实现

     @Override
    public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
      try {
        MappedStatement ms = configuration.getMappedStatement(statement);
        executor.query(ms, wrapCollection(parameter), rowBounds, handler);
      } 
      ....
    }

    executor才是和数据库交互的执行器,后面要将的MapperProxy代理实际上执行的就是sqlSession的方法,而sqlSession真正执行的是executor的方法

Mapper对象&MapperStatement

基本功能

mapper基本功能

Mapper接口,定义了mybatis中业务类操作数据库的相关方法,比如针对user对象操作的UserMapper

 UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
 List<UserEntity> userList = userMapper.listAllUser();

对于UserMapper接口来说,框架使用者并没有做具体的实现,框架使用者只定义了接口和UserMapper.xml配置文件,就能生成UserMapper的具体实现。在这里,Mybatis就使用了JDK的动态代理技术,根据UserMapper.xml和UserMapper接口生成UserMapper的动态代理实现就使用了

MapperStatement基本功能

看一下UserMapper.xml中的一个sql片段定义

<select id="listAllUser"  resultType="org.learning.entity.UserEntity" >
        select
        <include refid="userAllField"/>
        from user
    </select>

这么一个标签就定义了一个MapperStatement对象,还可以包含很多属性,具体对象的成员变量有

public final class MappedStatement {

  private String resource;
  private Configuration configuration;
  private String id;
  private Integer fetchSize;
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;
  private SqlSource sqlSource; 
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;
  }

用于描述这个标签,在Configuration中用Map成员变量来具体描述

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");

map的key就是MappedStatement描述的mapper的命名空间加sql段的id
案例 key:

org.learning.mapper.UserMapper.listAllUser

这样在执行UserMapper.listAllUser()方法的时候,需要找到具体的MappedStatement存储的信息来执行

创建源码

Mapper的创建源码

接Configuration章节的Mapper注册进Configuration,以mybatis-config.xml中Mapper的配置为包扫描为例

<mappers>
        <package name="org.learning.mapper"/>
</mappers>

解析生成Configuration对象时的会自动扫描

public void addMappers(String packageName) {
    mapperRegistry.addMappers(packageName);
  }

MapperRegistry的addMappers的实现

public void addMappers(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    for (Class<?> mapperClass : mapperSet) {
      addMapper(mapperClass);
    }
  }

即扫描这个package,然后for循环一个一个地添加mapper类:

addMapper的实现
public <T> void addMapper(Class<T> type) {
   .......
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
     .......
    }
  }

所以MapperRegistry里存储的是knownMappers是UserMapper的class和UserMapper的代理对象工厂MapperProxyFactory,当

 UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

时,就会从MapperProxyFactory创建MapperProxy的jdk动态代理对象,
MapperProxyFactory的newInstance()实现:

protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

MapperStatement的创建源码

MapperStatement的创建在mapper解析之后,在MapperRegistry的knownMappers缓存了MapperProxyFactory之后,接下来就是针对MapperStatement的解析了,MapperAnnotationBuilder的parse源码:

public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
      loadXmlResource();
      .......
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          if (!method.isBridge()) {
            parseStatement(method);
          }
        } 
      ......
    }
    parsePendingMethods();
  }

可以看出,优先从UserMapper.xml中<select|update|delete|insert>解析出MappedStatement对象,其次再从UserMapper的方法的注解如@SelectKey、@ResultMap的sql解析出MappedStatement

走进loadXmlResource();方法

private void loadXmlResource() {
    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
      String xmlResource = type.getName().replace('.', '/') + ".xml";
      InputStream inputStream = null;
      try {
        inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
      } catch (IOException e) {
        // ignore, resource is not required
      }
      if (inputStream != null) {
        XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
        xmlParser.parse();
      }
    }
  }

之后,就会根据注解或者xml标签属性填充MappedStatement注册进Configuration中。

不难发现,当UserMapper.xml和UserMapper.class在同一目录下,UserMapper.xml会被默认识别出来。这也就是我们在idea中放在resources下的mapper的xml文件也要有包名的原因。

Mapper的执行代码

接下来我们看一下mapper调用时的具体实现

 List<UserEntity> userList = userMapper.listAllUser();

从mapper的创建我们知道,userMapper实际上由MapperProxy代理,在执行方法时候

@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    .......
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

会选用MapperMethod来执行语句mapperMethod.execute()的实现:

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

最终还是调用到了sqlSession里面的方法。由此我们可以得出,Mapper也是通过sqlSession来操作数据库的。

StatementHandler

Configuration对象提供了几种handler的工厂方法,并且都会通过拦截器

 public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

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

接下来,我们介绍StatementHandler这种handler的功能和用法。

StatementHandler服务于mybatis框架流程到JDBC的Statement的转换,并且封装了jdbc中statement的基本操作比如执行语句等

public interface StatementHandler {
  Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
  void parameterize(Statement statement) throws SQLException;
  void batch(Statement statement) throws SQLException;
  int update(Statement statement) throws SQLException;
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;
  <E> Cursor<E> queryCursor(Statement statement) throws SQLException;
  BoundSql getBoundSql();
  ParameterHandler getParameterHandler();
}

我们可以看下StatementHandler基类的成员变量都是mybatis的成员变量和部分方法

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;
  protected BoundSql boundSql;
  ...
  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      return statement;
    ......
  }
  ...
  }

当mybatis中的对象准备调用jdbc方法时,调用prepare方法,根据不同的实现比如PreparedStatementHandler就可以生成PreparedStatement对象。然后就可以代用ParameterHandler对PreparedStatement中的占位符填充

请求组件调用链

从以上章节中我们了解到了组件的相关功能,接下来我们根据下面示例,看看mybatis在操作mybatis组件之间的轮转源码

public  void testMybatis () throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<UserEntity> userList = userMapper.listAllUser();
        System.out.println(userList);
    }

动态代理的调用

首先,SqlSessionFactoryBuilder读取了mybatis的主配置文件,并且在解析配置文件的时候把环境参数、插件、数据库配置、mapper配置、每一个sql段、都注册到内存里面。

sqlSession.getMapper(UserMapper.class);获取UserMapper的动态代理对象,

在执行userMapper的listAllUser();方法时,走到了MapperProxy的invoke方法里

@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    ...
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

由mapperMethod代为执行,调用了MapperMethod.execute()

代理方法的调用

MapperMethod的exec底层又是直接操作SqlSession对象的方法

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      ...
      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:
        .....
    return result;
  }

真正的执行器executor

在DefaultSqlSession的selectList中,获取到当前方法的MappedStatement对象,并把执行过程委托给了executor

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

executor操作缓存代码

executor的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);
  }
@Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
      ........
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

mybatis支持基于sqlSession的一级缓存和基于Mapper文件的namespace的二级缓存的,而cache缓存的调用实现就是CachingExecutor。

当没有命中缓存的时候,使用executor的实现SimpleExecutor,走到了基类BaseExecutor的queryFromDatabase,方法中,并进入了SimpleExecutor的doQuery方法中

转换成jdbc的Statement对象

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

有configuration创建了mybatis组件转换jdbc组件转换的StatementHandler,并且在prepareStatement(handler, ms.getStatementLog())方法中

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

查询完数据库解析结果

获取数据库连接并对PreparedStatement的值进行填充,
然后调用StatementHandler的query方法,查询数据库填充默认的ResultSetHandler返回数据结果。

这样mybatis的一整个调用就完成了。mybatis的代码小巧灵活、代码精湛、但篇幅有限,源码分析就到此结束,有很多mybatis的书籍,有兴趣的朋友可以再深入研究。


汤卜
33 声望1 粉丝