MyBatis源码分析(一)会话连接与调用流程分析

1、分析案例搭建

  • 新建一个maven项目
  • 引入依赖
			 <!-- Mybatis依赖 -->
	        <dependency>
	            <groupId>org.mybatis</groupId>
	            <artifactId>mybatis</artifactId>
	            <version>3.5.2</version>
	        </dependency>
	        <!-- MySQL依赖 -->
	        <dependency>
	            <groupId>mysql</groupId>
	            <artifactId>mysql-connector-java</artifactId>
	            <version>8.0.16</version>
	            <scope>runtime</scope>
	        </dependency>
  • 创建实体类
public class User implements Serializable {

    private Integer age;
    private String name;

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • 创建mapper
public interface UserMapper {
    /**
     * 根据id查询
     *
     * @param id
     * @return
     */
    User selectById(@Param("id") String id);
}

  • 创建mapper.xml
<?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="com.xiao7.mybatis.mapper.UserMapper">
    <resultMap id="User" type="com.xiao7.mybatis.entity.User">
        <id column="AGE" property="age"/>
        <!--        <result column="NAME" property="name"/>-->
    </resultMap>
    <cache/>
    <select id="selectById" resultMap="User">
        select * from user
        <where>
            <if test="id != null">
                id = #{id}
            </if>
        </where>
    </select>
</mapper>

  • 新建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="cacheEnabled" value="true"/>
    </settings>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8"/>
                <property name="username" value="root"/>
                <property name="password" value="Asd4840840"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/xiao7/mybatis/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

  • 测试类

    public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        SqlSession sqlSession = null;
        try {
            //读取mybatis-config.xml
            inputStream = Resources.getResourceAsStream(resource);
            //解析mybatis-config.xml配置文件,创建sqlSessionFactory
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSessionFactory.getConfiguration().addInterceptor(new ExecutorPlugin());
            sqlSessionFactory.getConfiguration().addInterceptor(new ParameterHandlerPlugin());
            sqlSessionFactory.getConfiguration().addInterceptor(new ResultSetHandlerPlugin());
            sqlSessionFactory.getConfiguration().addInterceptor(new StatementHandlerPlugin());


            //创建sqlSession
            sqlSession = sqlSessionFactory.openSession(true);
            //创建userMapper对象(UserMapper并没有实现类)
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //调用userMapper对象的方法
            User user = userMapper.selectById("1");
            System.out.println(user);

            // 提交
            sqlSession.commit();

            SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
            UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
            User user1 = userMapper1.selectById("1");
            System.out.println(user1);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            sqlSession.close();
        }
    }

2、流程源码分析

2.1、配置文件解析

首先的话是读取的config配置文件流,然后使用SqlSessionFactoryBuilder去解析构建sqlSessionFactory,直接进入方法查看它是如何解析并构建的。

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

SqlSessionFactory没有构造方法,那么这里使用的就是默认无参构造方法,所以我们直接进去build方法。

//这个方法啥也没干  
public SqlSessionFactory build(InputStream inputStream) {
    //调用的是另外一个build方法
    return build(inputStream, null, null);
 }

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //创建一个XMLConfigBuilder对象  
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // 主要的话是使用XMLConfigBuilder去解析出Configuration对象
      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);
 }

主要的话是使用XMLConfigBuilder去解析出Configuration对象,而XMLConfigBuilder里面主要是使用 Java XPath 解析器XPathParser去解析MyBatis中的mybatis-config.xml、mapper.xml等 XML 配置文件

先解析一级节点

public Configuration parse() {
  if (parsed) {
   throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  //mybatis-config.xml的一级标签
  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);
      loadCustomLogImpl(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);
    }
  }

只看一下mappers是怎么解析的吧

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        //自动扫描包下所有映射器
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          //放到配置对象configuration中  
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          //使用java类名
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
             //根据文件存放目录,读取XxxMapper.xml
            InputStream inputStream = Resources.getResourceAsStream(resource);
             //映射器比较复杂,调用XMLMapperBuilder
            //注意在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          //使用绝对url路径
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            //映射器比较复杂,调用XMLMapperBuilder
            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.");
          }
        }
      }
    }
  }

把每一个节点的配置解析到configuration中,然后构建factory,大致流程如下
在这里插入图片描述

2.2、开启sqlSession会话

解析完配置文件之后,就需要开启一个与数据库连接的会话,然后在执行后续的操作。也就是下面这一段代码:

 sqlSession = sqlSessionFactory.openSession(true);

直接查看会进入到:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      //对应xml标签<environments> ,这个在配置文件解析的时候就已经存放到configuration中了。
      final Environment environment = configuration.getEnvironment();
      //构建事务工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //构建一个事务对象  
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //创建一个executor来执行SQL  
      final Executor executor = configuration.newExecutor(tx, execType);
      //创建一个DefaultSqlSession对象并返回
      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();
    }
  }

1、创建事务Transaction

事务工厂类型可以配置为JDBC类型或者MANAGED类型。

  • JdbcTransactionFactory生产JdbcTransaction。
  • ManagedTransactionFactory生产ManagedTransaction。

如果配置的JDBC,则会使用Connection对象的commit()、rollback()、close()方法来管理事务。但是,如果是Spring+Mybatis,则没有必要配置,因为我们会直接在applicationContext.xml里配置数据源和事务管理器,从而覆盖Mybatis的配置。

把事务传给newExecutor()方法创建执行器Executor对象。

configuration.newExecutor(tx, execType)

2、创建Executor

调用configuration的newExecutor方法创建Executor。

final Executor executor = configuration.newExecutor(tx, execType);
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    // 1
    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);
    }
    // 2
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    // 3
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

主要有三步骤

  1. 根据不同的类型创建Executor,默认的话是SimpleExecutor
  2. 如果设置的启用缓存的话则用CachingExecutor包装一层
  3. 使用拦截器链包装Executor,如果有拦截器的话,Executor会在这一步被包装成代理对象返回

Executor创建完毕后,就该创建DefaultSqlSession了,请看代码:

//创建一个DefaultSqlSession对象并返回
return new DefaultSqlSession(configuration, executor, autoCommit);

进入DefaultSqlSession的构造方法中:

public class DefaultSqlSession implements SqlSession {
   private final Configuration configuration;
   private final Executor executor;
   public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
     this.configuration = configuration;
     this.executor = executor;
     this.dirty = false;
     this.autoCommit = autoCommit;
   }
}

大致流程如下:
在这里插入图片描述

2.3、mapper的代理对象

下一步则是获取mapper的对象了,因为mapper只是一个接口,所以毫无疑问,获取的就是一个代理的对象。

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

debug进去会到DefaultSqlSession

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

然后又由configuration去获取,又在交给mapperRegistry去获取

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  	// 获取到每个class的代理工厂
    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);
    }
  }

具体的代理源码:

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

 
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

就是新建一个MapperProxy代理的拦截器对象,然后使用jdk代理,所以mapper的调用最终都会经过MapperProxy的invoke方法,先提前看一下,源码如下:

 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 (method.isDefault()) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

整体流程大致如下:
在这里插入图片描述

2.4、执行流程

上文也提到了,mapper是一个代理对象了,debug也可以看到:
在这里插入图片描述
所以,我们直接进入,代理的invoke方法中开始:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
      //首先判断是否为Object本身的方法,是则不需要去执行SQL,
      //比如:toString()、hashCode()等方法。
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (method.isDefault()) {
        //判断是否JDK8以后的接口默认实现方法。
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    //<3>  
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //<4>
    return mapperMethod.execute(sqlSession, args);
}

然后是由mapperMethod去执行接口逻辑的,先了解一下它吧。MapperMethod这个类,定义了两个属性command和method,以及两个静态内部类。

  • SqlCommand封装了statement ID,比如说:
com.tian.mybatis.mapper.UserMapper.selectById
  • 和SQL类型。
public enum SqlCommandType {
UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
}

具体源码:

public class MapperMethod {
 private final SqlCommand command;
 private final MethodSignature method;
 public static class SqlCommand {
    private final String name;
    private final SqlCommandType type;
    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
      final String methodName = method.getName();
      final Class<?> declaringClass = method.getDeclaringClass();
      //获得 MappedStatement 对象
      MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
            configuration);
      // <2> 找不到 MappedStatement
      if (ms == null) {
        // 如果有 @Flush 注解,则标记为 FLUSH 类型
        if (method.getAnnotation(Flush.class) != null) {
            name = null;
            type = SqlCommandType.FLUSH;
        } else { 
            // 抛出 BindingException 异常,如果找不到 MappedStatement
            //(开发中容易见到的错误)说明该方法上,没有对应的 SQL 声明。
            throw new BindingException("Invalid bound statement (not found): "
                    + mapperInterface.getName() + "." + methodName);
        }
       //找到 MappedStatement
       } else {
        // 获得 name
        //id=com.tian.mybatis.mapper.UserMapper.selectById
        name = ms.getId();
        // 获得 type=SELECT
        type = ms.getSqlCommandType();
        //如果type=UNKNOWN
        if (type == SqlCommandType.UNKNOWN) { // 抛出 BindingException 异常,如果是 UNKNOWN 类型
            throw new BindingException("Unknown execution method for: " + name);
        }
       }
    }   
    private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
                                               Class<?> declaringClass, Configuration configuration) {
      // 获得编号
      //com.tian.mybatis.mapper.UserMapper.selectById
      String statementId = mapperInterface.getName() + "." + methodName;
      //如果有,获得 MappedStatement 对象,并返回
      if (configuration.hasStatement(statementId)) {
        //mappedStatements.get(statementId);  
        //解析配置文件时候创建并保存Map<String, MappedStatement> mappedStatements中
        return configuration.getMappedStatement(statementId);
      // 如果没有,并且当前方法就是 declaringClass 声明的,则说明真的找不到
      } else if (mapperInterface.equals(declaringClass)) {
        return null;
      }
      // 遍历父接口,继续获得 MappedStatement 对象
      for (Class<?> superInterface : mapperInterface.getInterfaces()) {
        if (declaringClass.isAssignableFrom(superInterface)) {
            MappedStatement ms = resolveMappedStatement(superInterface, methodName,
                    declaringClass, configuration);
            if (ms != null) {
                return ms;
            }
        }
      }
      // 真的找不到,返回 null
      return null;
   } 
    //....
 }
public static class MethodSignature {
    private final boolean returnsMap;
    private final Class<?> returnType;
    private final Integer rowBoundsIndex;
    //....
}

接着看MapperMethod中execute方法。

先来看看这个方法的整体逻辑:

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case SELECT:
         //部分代码省略
         Object param = method.convertArgsToSqlCommandParam(args);
          //本次是QUERY类型,所以这里是重点  
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    } 
    return result;
  }

这个方法中,根据我们上面获得的不同的type(INSERT、UPDATE、DELETE、SELECT)和返回类型:

  • 调用convertArgsToSqlCommandParam()将方法参数转换为SQL的参数。
  • 调用sqlSession的insert()、update()、dalete()、selectOne()方法。我们这个案例是查询,这里回到了DefaultSqlSession中selectOne方法中。

继续DefaultSqlSession中的selectOne()方法:

//DefaultSqlSession中  
@Override
public <T> T selectOne(String statement, Object parameter) {
    //这是一种好的设计方法
    //不管是执行多条查询还是单条查询,都走selectList方法(重点)
    List<T> list = this.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 {
      //数据库中没有数据就返回null
      return null;
    }
 }

这里调用的是selectList方法。

@Override
public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
 }
 @Override
 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //从configuration获取MappedStatement
      //此时的statement=com.tian.mybatis.mapper.UserMapper.selectById
      MappedStatement ms = configuration.getMappedStatement(statement);
      //调用执行器中的query方法
      return executor.query(...);
    } catch (Exception e) {
     //.....
    } finally {
      ErrorContext.instance().reset();
    }
 }

在这个方法里是根据statement从configuration对象中获取MappedStatement。

MappedStatement ms = configuration.getMappedStatement(statement);

在configuration中getMappedStatement方法:

//存放在一个map中的
//key是statement=com.tian.mybatis.mapper.UserMapper.selectById,value是MappedStatement
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>();  
  public MappedStatement getMappedStatement(String id) {
    return this.getMappedStatement(id, true);
  }
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) { 
    return mappedStatements.get(id);
}

而MappedStatement里面有xml中增删改查标签配置的所有属性,包括id、statementType、sqlSource、入参、返回值等。

最后sql的执行都是交给executor来执行的,接着看,如果executor有拦截器的话则会先走Plugin的invoke方法,如果没有则会先走executor的query方法

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  	// 获取sql对象
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // 创建缓存key
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

因为开启的缓存,所以调用的cacheExecutor的具体执行是由delegate这个simpleExecutor去执行

  @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) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        // 从二级缓存中获取
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          // 没有缓存则调用原Executor去执行sql
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.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;
  }

接着看它是如何从数据库中查询并组装数据的:

private <E> List<E> queryFromDatabase(...) 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);
    return list;
}
@Override
public <E> List<E> doQuery(....) throws SQLException {
    Statement stmt = null;
    try {
      //获取配置文件信息  
      Configuration configuration = ms.getConfiguration();
      //获取handler
      StatementHandler handler = configuration.newStatementHandler(....);
      //获取Statement
      stmt = prepareStatement(handler, ms.getStatementLog());
      //执行RoutingStatementHandler的query方法  
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
}

最后到StatementHandler执行:

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //JDBC的流程了  
    ps.execute();
    //处理结果集,如果有插件代理ResultHandler,会先走到被拦截的业务逻辑中
    return resultSetHandler.handleResultSets(ps);
}

看到了ps.execute();表示已经到JDBC层面了,这时候SQL就已经执行了。后面就是调用DefaultResultSetHandler类进行处理。结果集的映射是调用resultType的类构建对象,根据mapper填充数据返回,默认是根据字段赋值的,不设置也是可以的。

到这里,SQL语句就执行完毕,并将结果集赋值并返回了。

大致流程如下:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
引用中提到,Mybatis一个可以使用简单的XML或者注解来配置和映射原生信息的框架,它可以将接口和Java的POJO映射成数据库中的记录。同时,Mybatis支持定制化SQL、存储过程以及高级映射,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集的繁琐操作。 要进行Mybatis源码分析,需要深入研究Mybatis的核心组件和原理。其中,SqlSessionFactoryBuilder用于构建SqlSessionFactory,SqlSessionFactory负责创建SqlSession,SqlSession是与数据库交互的主要接口,通过SqlSession可以执行SQL语句并获取结果。在SqlSession的底层,涉及到Executor、StatementHandler、ParameterHandler和ResultSetHandler等核心组件。 Executor负责执行SQL语句,StatementHandler负责处理SQL语句的预编译和参数设置,ParameterHandler负责处理SQL语句的参数传递,ResultSetHandler负责处理SQL语句的结果集。Mybatis通过这些核心组件的协作来完成数据库操作。 在具体操作时,Mybatis的SQL映射文件(或注解)中定义了SQL语句和参数映射关系,Mybatis会根据配置的Mapper接口和对应的SQL语句,动态生成Mapper接口的实现类。通过动态代理的方式,实现了Mapper接口的方法与SQL语句的绑定,使得开发者可以直接调用Mapper接口的方法来执行SQL语句。 总之,Mybatis源码分析需要深入了解其核心组件和原理,包括SqlSessionFactory、SqlSession、Executor、StatementHandler、ParameterHandler和ResultSetHandler等。通过分析这些组件的工作原理和协作关系,可以更好地理解Mybatis的内部实现机制。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Mybatis源码分析](https://blog.csdn.net/zyyforever/article/details/101289858)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值