记一次MyBatis源码的阅读

阅读源码是一种好的习惯,也许现在看起来很吃力,然后就大不了多看几遍即可。


首先clone MyBatis的源码到自己的本地电脑,便于打印参数和修改值等调试。当然你可以直接引入依赖打断点进行调试是一样。

当然我这里是导入 源码到自己的项目中进行修改和调试。贴上圈出来的代码.

package org.gavin;

/*
 *@author:BaoYang
 *@Date: 2020/3/1
 *@description:
 */

public class Blog
{
    private Integer id;
    private String name;

    public Integer getId()
    {
        return id;
    }

    public void setId(Integer id)
    {
        this.id = id;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    @Override
    public String toString()
    {
        return "Blog{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
package org.gavin;

/*
 *@author:BaoYang
 *@Date: 2020/3/1
 *@description:
 */

public interface BlogMapper
{

    Blog selectBlog(int id);

}

 

<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/BlogMapper.xml"/>
    </mappers>
</configuration>

 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.gavin.BlogMapper">
    <select id="selectBlog" resultType="org.gavin.Blog">
      select * from t_user where id = #{id}
    </select>
</mapper>

 

package org.gavin;

/*
 *@author:BaoYang
 *@Date: 2020/3/1
 *@description:
 */

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class BlogMain
{

    public static void main(String[] args) throws Exception
    {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        try(SqlSession session = sqlSessionFactory.openSession()) {
            // Configuration configuration = session.getConfiguration();
            // configuration.getMappedStatement("org.gavin.BlogMapper");

            BlogMapper blogMapper = session.getMapper(BlogMapper.class);
            Blog blog = blogMapper.selectBlog(1);
            System.out.println(blog.toString());
        }
    }

}

这样你就可以打断点的方式来调试程序了。


源码阅读理解基于个人:

InputStream inputStream = Resources.getResourceAsStream(resource);这行代码最后走到下面的地方,就是读取出文件并返回回去。
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
  InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
  if (in == null) {
    throw new IOException("Could not find resource " + resource);
  }
  return in;
}

 

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

这行代码走到  org.apache.ibatis.session.SqlSessionFactoryBuilder

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // JSONObject parseJson = JSONObject.parseObject(JSONObject.toJSONString(parser));
      // System.out.println("parseJson value is ---> " + parseJson);

      Configuration configuration = parser.parse();
      return build(configuration);
    } 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.
      }
    }
  }

 

首先根据传入进来的流,构建出一个 XMLConfigBuilder的对象出来,然后调用parse()方法,并且返回Configuration对象.我们点进去Parse方法中. 

 parsed 属性默认是false,然后进来后修改true,可能看到如果是true的话,就会抛出异常。确保只执行一次。

 获取 /configuration节点下的对应的值.调用  parseConfiguration 方法.我们继续跟

  public Configuration parse() {
    // parsed  判断是否解析过
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // 获取出节点信息
    XNode xNode = parser.evalNode("/configuration");
    parseConfiguration(xNode);
    return configuration;
  }

 

这里看到的这些值, properties,settings,等是不是非常的熟悉,就是我们在mybatis-config.xml中可以配置的一些属性字段等。然后可以看到是挨个进行处理的,然后处理的顺序也是一目了然。这里我们直接看mapperElement(root.evalNode("mappers"))就可以,然后我们继续跟进。

  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      // 挨个解析 xml中的配置信息
      XNode propertiesXNode = root.evalNode("properties");
      // System.out.println("propertiesXNode.toString() ---> " + propertiesXNode);
      propertiesElement(propertiesXNode);

      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);

      typeAliasesElement(root.evalNode("typeAliases"));

      pluginElement(root.evalNode("plugins"));

      objectFactoryElement(root.evalNode("objectFactory"));

      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

      reflectionFactoryElement(root.evalNode("reflectionFactory"));

      settingsElement(settings);

      // read it after objectFactory and objectWrapperFactory issue #631
      XNode environments = root.evalNode("environments");
      // System.out.println("environments.toString() ---> " + environments.toString());
      environmentsElement(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);
    }
  }

 

  可以看到这个方法是在for循环中进行处理,这个很简单,因为你会有多个***Mapper.xml文件嘛,所以这里是for。

  首先判断是不是用package去映射一个包下的所有的XML.当然我们这里并不是的。

  然后会获取出 resource / url / class 等响应的属性, 然后根据者三个属性是否有值去走对应的逻辑判断。

我们这里的话会走这个逻辑 resource != null && url == null && mapperClass == null

  然后我们直接点入 mapperParser.parse() 来跟进代码。

  先判断 configuration中是否加载过这个,如果没有就进行加载,并且添加到configuration的集合记录中去。

  这里我们继续跟进  configurationElement方法

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      // 遍历,因为有多个mapper,并不是只有一个
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");

          // resource 方式解析xml
          if (resource != null && url == null && mapperClass == null) {
            // 封装错误信息,对应的mapper
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration,
                    resource, configuration.getSqlFragments());

            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }

        }
      }
    }
  }


  // 继续跟进的代码
  public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      // xml 完全解析了
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);

      bindMapperForNamespace();
    }
    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
  }

  

  这里可以看到获取一些属性。然后我们继续跟最后一行代码 buildStatementFromContext 可以看到,一个字符串分别传入 Select/Insert/Update/Delete 就可以看到分别对应我们的 查询,插入,修改,删除.然后我们继续跟.

  private void configurationElement(XNode context) {
    try {
      // namespace
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));

      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

 

    我们跟到最后是可以发现,创建了一个MappedStatement 的对象,然后封装到 Configuration的Map中经行维护。

  private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }
  

  // 跟
  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    // 获取 INSERT / UPDATE /DELETE / SELECT
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant,
              context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }


// 跟
  public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    Class<?> resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
    }

    // 最后都封装到 MapperStatement 对象中, 每个Sql 封装一个
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

  // 跟
  public MappedStatement addMappedStatement(
      String id,
      SqlSource sqlSource,
      StatementType statementType,
      SqlCommandType sqlCommandType,
      Integer fetchSize,
      Integer timeout,
      String parameterMap,
      Class<?> parameterType,
      String resultMap,
      Class<?> resultType,
      ResultSetType resultSetType,
      boolean flushCache,
      boolean useCache,
      boolean resultOrdered,
      KeyGenerator keyGenerator,
      String keyProperty,
      String keyColumn,
      String databaseId,
      LanguageDriver lang,
      String resultSets) {

    if (unresolvedCacheRef) {
      throw new IncompleteElementException("Cache-ref not yet resolved");
    }

    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resulSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);

    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
      statementBuilder.parameterMap(statementParameterMap);
    }

    // 通过 build 方法构建出一个 MapperStatement 对象
    MappedStatement statement = statementBuilder.build();
    //  key : id   value : statement, 存放在 Map 集合中
    configuration.addMappedStatement(statement);
    return statement;
  }

这里基本的初步解析算是做完了。主要还是可以看到,读取对应的配置文件,解析数据,然后装置到自己准备的对象中。并且返回了SqlSessionFacotry接口,具体的是接口的实现类 DefaultSqlsessionFactory.


 

 接下来我们继续看 :   跟进到 源码 DefaultSqlSessionFactory来。

SqlSession session = sqlSessionFactory.openSession()
  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); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }


// 跟进
   public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    // 三种生成器  SIMPLE BATCH  REUSE
    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-config.xml中的配置信息都保存在 configuration 对象中, 获取Environment节点的信息,这个节点信息配置了数据库连接和事务。 然后getTransactionFactoryFromEnvironment创建一个事务工厂.

  Note : 创建一个Executor执行器,我们知道session是与数据库交互的顶层Api,session 中会维护一个Executor来负责执行sql生成和执行等。 然后我们继续跟进到newExecutor 方法中, 这是有三种执行器

  1.   SimpleExecutor: 简单执行器,是MyBatis中默认使用的执行器.每次执行 update / select,就开启一个Statement对象,用完就直接关闭Statement
  2.   ReuseExecutor:  可重用执行器。内部使用 HashMap把创建的Statement缓存起来,每次执行Sql的时候,都回去HashMap中判断下是否已经存在。如果存在并且之前的connection还没有关闭的情况下会继续使用之前的statement
  3.  BatchExecutor:  批量处理执行器,用于将多个SQL一次性输出到数据库。

 最后返回一个 DefaultSqlSession.


session.getMapper(BlogMapper.class) : 

 

// org.apache.ibatis.binding.MapperRegistry  
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);
    }
  }

 

  我们跟进到源码的这个地方来,就可以看出来,最后返回的是一个代理对象,可以很清楚的在MapperProxy@.... 这里主要就看给看下使用的代理。


Blog blog  = blogMapper.selectBlog(1)  这里的源码就是sql执行的,我们直接跟进去看主要的

// MapperMethod 类中
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    if (SqlCommandType.INSERT == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) {
      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 {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else if (SqlCommandType.FLUSH == command.getType()) {
        result = sqlSession.flushStatements();
    } else {
      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;
  }


// 跟  DefaultSqlSession
  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

// DefaultSqlSession 跟
  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      // 拿取出 Statement
      MappedStatement ms = configuration.getMappedStatement(statement);
      Object collection = wrapCollection(parameter);
      return executor.query(ms, collection, rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

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



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


// SimpleExecutor  跟
  @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());
      List<E> list = handler.<E>query(stmt, resultHandler);
      return list;
    } finally {
      closeStatement(stmt);
    }
  }



 

 

 

  1.  MapperStatement ms = configuration.getMappedStatement(statement) 如果往上面可以看到,有说过对应的Sql甚恶都封装在Mapperstatement对象, 然后封装到 Configuration中的一个 Map进行存储。Key : Sql语句Id,value 就是对应MapperStatement对象。
  2.  可以看到 Debug 走的类的流程, CacheingExecuotr 走的这个执行器,所以代码也是跟进到这个类进行看。可以从出来,从 Mapperstatement中获取出BoundSql.
  3.  然后我们看到 delegate.query() 中调用的使用。 如果没有配置的话,就是默认使用 SimpleExecutor,这里可以看到把SimpleExecutor的引用维护到CachingExecutor中。这里实际用到了 委托者模式, 我没时间做或者我做不了的事情,于是就找一个可以做的来做。
  4.  最后我们往下面继续跟进, queryFromDataBase  ---> doQuery  ---> doQuery() 。 最后我们可以看到,在doQuery中的prepareStatement中可以看到熟悉的东西。
  5. 最后可以看到 返回的 list中的值,就是我数据库中的 diaoge.到这里大致的流程是基本的OK的。

 

 大致的源码流程就是这样,当然里面还有很多细节的东西需要时间慢慢去理解和消费。这里仅仅一点点的小皮毛。同样的Delete/Update/Insert也可以使用同样的方式来进行分析。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值