0
点赞
收藏
分享

微信扫一扫

mybatis查询过程源码解析




    



本文只讲解mybatis的源码 ,不涉及过多的用法讲解,用法请百度。




mybatis查询执行步骤(举SELECT分析)



第一步:获取mapperProxy



第二步:获取mapperMethod



第三步:找到对应的执行类型(SELECT\UPDATE\INSERT\DELETE)



第四步:查询前的缓存处理,判断是否需要从二级、一级缓存中取数据(先二级再一级)



第五步:真正调用用jdbc调数据库,先预编译、再注入参数



第六步:进行结果集解析填充




开发mybatis涉及的坑



一二级缓存最好都关闭掉



select多个报selectOne操作



mapper.xml与mapper.java的类型不一致导致返回类型错误



#与$区别




看源码前各个名词介绍



1 mappedStatement



在程序启动扫描加载的时候,会把所有mapper的信息存储在mappedStatements上。可通过 全限定类名+方法名 或 方法名 获取对应mappedStatement



``````
       
 
       

        public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
       
 
       

           ............
       
 
       
// 这里 id = com.example.app.mapper.UserMapper.selectById
 
       

          return mappedStatements.get(id);
       
 
       

        }
       
 
       

        ``````




需要注意什么?



- userMapper.java接口里面不能实现同名函数重载,不然MappedStatement会冲突




mappedStatement里面有什么数据?



查询类型、数据来源、xml位置、id、参数类型信息、结果类型信息




2 TypeHandler



处理参数和结果集




可自定义实现一个typeHandler,继承BaseTypeHandler来自定义一个typeHandler类型




其默认常见的功能是自动映射参数类型(映射枚举类:JdbcType.java),举例如下:



- select user_id,user_name from lw_user where user_name=#{userName}


- 其实会自动映射成:select user_id,user_name from lw_user where user_name=#{userName,jdbcType=VARCHAR}



用法可参考:




下面举一个mybatis 查询demo来进行讲解:



public static void 
       main
       (String[] args) 
       throws 
       IOException {
 
      
    String resource = 
       "mybatis-config.xml"
       ;
 
      
    InputStream inputStream = Resources.
       getResourceAsStream
       (resource)
       ;
 
      
    SqlSessionFactory sqlSessionFactory = 
       new 
       SqlSessionFactoryBuilder().build(inputStream)
       ;
 
      
    
       // 通过SqlSessionFactory获取sqlSession
 
      
    try 
       (SqlSession session = sqlSessionFactory.openSession()) {
 
      
    
           
       // 通过SqlSession对象获取UserMapper接口的代理对象,
 
      
        // 然后赋值给UserMapper接口
 
      
        UserMapper mapper = session.getMapper(UserMapper.
       class
       )
       ;
 
      
    
           
       // 调用UserMapper接口的方法来执行指定的数据库操作
 
      
        System.
       out
       .println(mapper.selectById(
       1
       ))
       ;
 
      
    }
 
      
}





第一步:获取mapperProxy



触发时机:UserMapper mapper = session.getMapper(UserMapper.class)




其加载过程为:



第一步: 从configuration中获取mapperProxy代理对象



第二步: 用knownMappers.get(type)获取对应的代理工厂,这里拿到的的是UserMapper代理工厂,然后用jdk动态代理生成UserMapper的MapperProxy代理对象并返回。



接下来我们一步步进行解析:



// 1 从configuration中获取mapperProxy代理对象
      
 
      

       // - 所在类:DefaultSqlSession
      
 
      
public 
       <
       T
       > 
       T 
       getMapper
       (Class<
       T
       > type) {
 
      
    return this
       .
       configuration
       .getMapper(type
       , this
       )
       ;
 
      
}
 
       
      
..........
 
      

       // 2 用
       knownMappers.get(type)获取对应的代理工厂,这里拿到的的是UserMapper代理工厂
 
      
//      然后用jdk动态代理生成UserMapper的MapperProxy代理对象并返回。。
 
      

       // - 所在类:
       MapperRegistry.java
      
 
      
public 
       <
       T
       > 
       T 
       getMapper
       (Class<
       T
       > type
       , 
       SqlSession sqlSession) {
 
      

           /**
      
 
      

           * 
       加载
       mybatis-config.xml
       配置的
       <mapper>
       配置,根据指定
       type
       ,查找对应的
       MapperProxyFactory
       对象
      
 
      

           **/
      
 
      
    // eg1: 
       获得
       UserMapper
       的
       mapperProxyFactory
 
      
    final 
       MapperProxyFactory<
       T
       > mapperProxyFactory = (MapperProxyFactory<
       T
       >) 
       knownMappers
       .get(type)
       ;
 
      

           /**
      
 
      

           * 
       如果没配置
       <mapper>
       ,则找不到对应的
       MapperProxyFactory
       ,抛出
       BindingException
       异常
      
 
      

           */
      
 
      
    if 
       (mapperProxyFactory == 
       null
       ) {
 
      
        throw new 
       BindingException(
       "Type " 
       + type + 
       " is not known to the MapperRegistry."
       )
       ;
 
      
    }
 
      
    try 
       {
 
      

               /** 
       使用该工厂类生成
       MapperProxy
       的代理对象
       */
      
 
      
        return 
       mapperProxyFactory.
       newInstance
       (sqlSession)
       ;
 
      
    } 
       catch 
       (Exception e) {
 
      
        throw new 
       BindingException(
       "Error getting mapper instance. Cause: " 
       + e
       , 
       e)
       ;
 
      
    }
 
      
}
 
      

       /**
      
 
      

       * 
       通过动态代理,创建
       mapperInterface
       的代理类对象
      
 
      

       *
      
 
      

       * 
       @param 
       mapperProxy
      
 
      

       */
      
 
      
@SuppressWarnings
       (
       "unchecked"
       )
 
      
protected 
       T 
       newInstance
       (MapperProxy<
       T
       > mapperProxy) {
       // 这里就是JDK动态代理生成的userMapper的mapperProxy代理对象
 
      
    return 
       (
       T
       ) Proxy.
       newProxyInstance
       (
       mapperInterface
       .getClassLoader()
       , new 
       Class[] {
       mapperInterface
       }
       , 
       mapperProxy)
       ;
 
      
}
 
      
public 
       T 
       newInstance
       (SqlSession sqlSession) {
 
      

           /**
      
 
      

           * 
       创建
       MapperProxy
       对象,每次调用都会创建新的
       MapperProxy
       对象,
       MapperProxy implements InvocationHandler
      
 
      

           */
      
 
      
    final 
       MapperProxy<
       T
       > mapperProxy = 
       new 
       MapperProxy<
       T
       >(sqlSession
       , 
       mapperInterface
       , 
       methodCache
       )
       ;
 
      
    return 
       newInstance(mapperProxy)
       ;
 
      
}




第一步核心是:获取MapperProxy。其时序图如下:




mybatis查询过程源码解析_Java


第二步: 获取 mapperMethod


触发时机:List<User> users = mapper.selectByIds(1);


调用上面,就会触发到MapperProxy#invoke()方法。


为什么调用mapper.selectByIds(1)会触发到invoke()方法?


因为获取到的mapper是UserMapper的MpaerProxy代理,并且MaperProxy实现了InvocationHandler接口,实现了invoke方法,我们就可以在invoke方法自定义加载逻辑。


- public class MapperProxy<T> implements InvocationHandler, Serializable {....}


其加载过程为:


第一步:从methodCache缓存中获取,没有就创建一个put进去


第二步:创建mapperMethod过程中,会涉及到两个重要成员属性:


- 从反射的method中获取信息,然后存放到mapperMethod中。MapperMethod结构如下:


``````java
        
public class MapperMethod {
        
    // 记录了SQL语句的名称和类型
        
command;
        
    // Mapper接口中对应方法的相关信息
        
method;
        
    ................
        
}
        
``````
        
- commond:
        
--  name属性:MappedStatement的唯一标识id
        
--  type属性:SQL的命令类型UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH
        
- method(总体是 判断返回类型、入参、下标号、注解值):
        
--  returnsMany:判断返回类型是集合或者数组吗
        
--  returnsMap:判断返回类型是Map类型吗
        
--  returnsVoid:判断返回类型是集void吗
        
--  returnsCursor:判断返回类型是Cursor类型吗
        
--  Class<?> returnType:方法返回类型
        
--  String mapKey:获得@MapKey注解里面的value值
        
--  Integer resultHandlerIndex:入参为ResultHandler类型的下标号
        
--  Integer rowBoundsIndex:入参为RowBounds类型的下标号
        
--  ParamNameResolver paramNameResolver:入参名称解析器
               
接下来我们一步步进行解析:
       

       // 第一步:
       从
       methodCache缓存中获取,没有就创建一个put进去
      
       
@Override
       
public        Object 
       invoke
       (Object proxy
       , 
       Method method
       , 
       Object[] args) 
       throws 
       Throwable {
       
    try        {
       

               /**        如果被代理的方法是
       Object
       类的方法,如
       toString()
       、
       clone()
       ,则不进行代理        */
      
       
        // eg1: method.getDeclaringClass()==interface mapper.UserMapper        由于被代理的方法是
       UserMapper
       的
       getUserById
       方法,而不是
       Object
       的方法,所以返回
       false
       
        if        (Object.
       class
       .equals(method.getDeclaringClass())) {
       
            return        method.invoke(
       this, 
       args)
       ;
       
        }
       

               /**        如果是接口中的
       default
       方法,则调用
       default
       方法        */
      
       
        else if        (isDefaultMethod(method)) { 
       // eg1: 
       不是
       default
       方法,返回
       false
       
            return        invokeDefaultMethod(proxy
       , 
       method
       , 
       args)
       ;
       
        }
       
    }        catch 
       (Throwable t) {
       
        throw        ExceptionUtil.
       unwrapThrowable
       (t)
       ;
       
    }
       
    // eg1: method = public abstract vo.User mapper.UserMapper.getUserById(java.lang.Long)
       

           /**        初始化一个
       MapperMethod
       并放入缓存中 或者 从缓存中取出之前的
       MapperMethod */
      
       
    final        MapperMethod mapperMethod = cachedMapperMethod(method)
       ;
      
              
    / eg1: sqlSession = DefaultSqlSession@1953 args = {2L}
       

           /**        调用
       MapperMethod.execute()
       方法执行
       SQL
       语句        */         // 这里是后面要说的逻辑,先不看
      
       
    return        mapperMethod.execute(
       sqlSession
       , 
       args)
       ;
       
}
       

       // 
       第二步:创建mapperMethod过程中,会涉及到两个重要成员属性。 从反射的method中获取信息,然后存放到mapperMethod中
      
       
private        MapperMethod 
       cachedMapperMethod
       (Method method) {
       

           /**
      
       

           *        在缓存中查找
       MapperMethod
       ,若没有,则创建
       MapperMethod
       对象,并添加到
       methodCache
       集合中缓存
      
       

           */
      
       
    // eg1:        因为
       methodCache
       为空,所以
       mapperMethod
       等于
       null
       
    MapperMethod mapperMethod =        methodCache
       .get(method)
       ;
       
    if        (mapperMethod == 
       null
       ) {
       
        // eg1:        构建
       mapperMethod
       对象,并维护到缓存
       methodCache
       中
       
        mapperMethod =        new 
       MapperMethod(
       mapperInterface
       , 
       method
       , 
       sqlSession
       .getConfiguration())
       ;
       
        // eg1: method = public abstract vo.User mapper.UserMapper.getUserById(java.lang.Long)
       
        methodCache       .put(method
       , 
       mapperMethod)
       ;
       
    }
       
    return        mapperMethod
       ;
       
}


第二步核心是:获取MapperMethod。其时序图如下:



mybatis查询过程源码解析_一级二级缓存_02


第三步:找到对应的执行类型(SELECT\UPDATE\INSERT\DELETE)


触发时机:MapperProxy.invoke()方法里面执行 mapperMethod.execute(sqlSession, args);


``````java
      
       
@Override
       
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       
    .........其他执行逻辑
       
// 前面已经获取到了MaperProxy和MapperMethod
       
    /** 调用MapperMethod.execute()方法执行SQL语句 */
       
mapperMethod.execute(sqlSession, args);
       
}
       

       ``````


其加载过程为(下面举例 SELECT, 所以下面只会讲解SELECT怎么查询,其他类型基本差不多 ):


第一步:调用MapperMethod.execute()方法执行SQL语句


第二步:在switch(command.type)中找到对应的类型(SELECT\UPDATE\DELETE\INSERT\FLUSH),并执行


接下来我们一步步进行解析:


// 
      第一步:
      调用MapperMethod.execute()方法执行SQL语句
     
      
@Override
      
public       Object       invoke
      (Object proxy
      , 
      Method method
      , 
      Object[] args) 
      throws 
      Throwable {
      
    ........
      

          /**       调用
      MapperMethod.execute()
      方法执行
      SQL
      语句       */        // 真正出发选择执行对应类型的入口
     
      
    return       mapperMethod.execute(      sqlSession
      , 
      args)
      ;
      
}
            

      /**

  第二步:在switch(command.type)中找到对应的类型(SELECT\UPDATE\DELETE\INSERT\FLUSH),并执行


* MapperMethod 采用命令模式运行,根据上下文跳转,它可能跳转到许多方法中。实际上它最后就是通过 SqlSession 对象去运行对象的 SQL


*/
     
      
public       Object       execute
      (SqlSession sqlSession
      , 
      Object[] args) {
      
    Object result      ;
      
    // eg1: command.getType() = SELECT
      
    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
      :
      
                                             
      // eg1: method.returnsVoid() = false method.hasResultHandler() = false
      
                                             
      if 
      (
      method
      .returnsVoid() && 
      method
      .hasResultHandler()) {
      
                                                     
      executeWithResultHandler(sqlSession
      , 
      args)
      ;
      
                                                     
      result = 
      null;
      
            }       else if       (
      method
      .returnsMany()) { 
      // 
      查询多条数据
      List<User> users = mapper.selectByIds(1);
      
                                                     
      result = executeForMany(sqlSession
      , 
      args)
      ;
      
                                             
      } 
      else if 
      (
      method
      .returnsMap()) { 
      // 
      查询返回
      map
      ,一般不建议这样用:
      Map map = mapper.selectMapById(1);
      
                                                     
      result = executeForMap(sqlSession
      , 
      args)
      ;
      
                                             
      } 
      else if 
      (
      method
      .returnsCursor()) { 
      // 游标,一般也不用,不用管
      
                                                     
      result = executeForCursor(sqlSession
      , 
      args)
      ;
      
                                             
      } 
      else 
      {
      // 
      mapper.selectById(1)会快走到这一步
     
      
                                              //       查询单个数据:
      User user = mapper.selectById(1);
      
                                                     
      /**       将参数转换为
      sql
      语句需要的入参       */
     
      
                                                     
      Object param = 
      method
      .convertArgsToSqlCommandParam(args)
      ;
      
                                                     
      /**       执行
      sql
      查询操作       */
     
      
               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      ;
      
}
      
// 发现selectOne最终也会调用到selectList,不过限制了只能返回一条记录。所以如果不确定是否只有一条,查询单条数据的时候最好加上limit 1
      
@Override
      
public       <      T
      > 
      T 
      selectOne
      (String statement
      , 
      Object parameter) {
      
          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       {
      
        return null;
      
    }
      
}


第三步核心是: 找到对应的执行类型(SELECT\UPDATE\INSERT\DELETE) 。其时序图如下:



mybatis查询过程源码解析_源码解析_03



第四步:查询前的缓存处理, 判断是否需要从二级、一级缓存中取数据(先二级再一级)


触发时机:DefaultSqlSession的方法  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)


其加载过程为 :


第一步:根据mappedStatements.get(id)获取对应的mappedStatement


第二步:对参数进行处理,对Collection、Array加多一层映射。下面举两个例子:


--  selectById(@Param("id") Integer id):默认会给我们加一个 {"param1":1, "id":1}映射,其中param1是自动生成的


--  selectById(@Param("ids") List<Integer> ids):默认会给我们加一个 {"list":[1], "ids":[1]}映射,其中list是自动生成的


第三步:解析出boundSql,里面包含参数映射,待执行sql等。并获取缓存cacheKey,进入待行方法query()


--  boundSql:包含待执行sql,参数映射等


--  cacheKey:会拼接出大概这种"619109510:2666897233:com.example.app.mapper.UserMapper.selectById:0:2147483647:select id,name from userinfo where id = ? limit 1:1:development"


第四步:进行缓存处理。先处理二级,再处理一级缓存


--  二级缓存:需要xml配置了<cache/>来开启


--  一级缓存:默认开启,如果开启statement模式则会每次都刷出不进行缓存存储



mybatis查询过程源码解析_一级二级缓存_04

mybatis查询过程源码解析_localCache_05


接下来我们一步步进行解析:


// 
     第一步:根据
     mappedStatements.get(id)获取对应的mappedStatement。
    
     
@Override
     
public      <     E     > List<     E
     > 
     selectList
     (String statement
     , 
     Object parameter
     , 
     RowBounds rowBounds) {
     
    .........
     
    MappedStatement ms =      configuration     .getMappedStatement(statement)     ;// 获取mappedStatements
     
    return      executor     .query(ms     ,      wrapCollection(parameter)
     , 
     rowBounds
     , 
     Executor.
     NO_RESULT_HANDLER
     )
     ;
     
         .........
     
}
     

     // 
     第二步:对参数进行处理,对
     Collection、Array加多一层映射
    
     
private      Object      wrapCollection     (     final 
     Object object) {
     
    if      (object      instanceof      Collection) {
     
        StrictMap<Object> map =      new      StrictMap<Object>()     ;
     
        map.put(     "collection"     ,      object)     ;
     
                  // 如果是Collection类型,就给他加一个list映射,所以平时我们就算不给类似selectById(List<Integer> list)加一个@Param("list")都能用默认的list访问
     
             if      (object      instanceof      List) {
     
            map.put(     "list"     ,      object)     ;
     
        }
     
        return      map     ;
     
    }      else if      (object !=      null      && object.getClass().isArray()) {
     
                  // 同上。不过这里加的是array
     
        StrictMap<Object> map =      new      StrictMap<Object>()     ;
     
        map.put(     "array"     ,      object)     ;
     
        return      map     ;
     
    }
     
         // 其他情况,举例:selectById(@Param("id") Integer id);默认会给我们加一个 {"param1":1, "id":1}映射,其中param1是自动生成的
     
    return      object     ;
     
}
     

     // 
     第三步:解析出boundSql,里面包含参数映射,待执行sql等。并获取缓存cacheKey,进入待行方法query()
    
     
public      <     E     > List<     E
     > 
     query
     (MappedStatement ms
     , 
     Object parameterObject
     , 
     RowBounds rowBounds
     , 
     ResultHandler resultHandler) 
     throws 
     SQLException {
     

         /**      获得
     boundSql
     对象,承载着
     sql
     和对应的参数
     */
    
     
    BoundSql boundSql = ms.getBoundSql(parameterObject)     ;
     

         /**      生成缓存
     key */
    
     
    CacheKey key = createCacheKey(ms     ,      parameterObject     ,      rowBounds
     , 
     boundSql)
     ;
     

         /**      执行查询语句      */
    
     
    return      query(ms     ,      parameterObject     , 
     rowBounds
     , 
     resultHandler
     , 
     key
     , 
     boundSql)
     ;
     
}
     
// 第四步:进行缓存处理。先处理二级,再处理一级缓存
     
//     --  二级缓存:需要xml配置了<cache/>来开启 
     
//      --  一级缓存:默认开启,如果开启statement模式则会每次都刷出不进行缓存存储
     
public      <     E     > List<     E
     > 
     query
     (MappedStatement ms
     , 
     Object parameterObject
     , 
     RowBounds rowBounds
     , 
     ResultHandler resultHandler
     , 
     CacheKey key
     , 
     BoundSql boundSql) 
     throws 
     SQLException {
     
    Cache cache = ms.getCache()     ;
     
    // eg1: cache = null
     

         /**      如果在
     UserMapper.xml
     配置了
     <cache/>
     开启了二级缓存,则
     cache
     不为
     null*/
    
     
    if      (cache !=      null     ) {
     

             /**
    
     

              *      如果
     flushCacheRequired=true
     并且缓存中有数据,则先清空缓存
    
     

         
         
     *
    
     

         
         
     *      <select id="save" parameterType="XXXXXEO" statementType="CALLABLE" flushCache="true" useCache="false">
    
     

         
         
     * ……
    
     

         
         
     *      </select>
    
     

         
         
     * */
    
     

         
         
     flushCacheIfRequired(ms)
     ;
          

         
         
     /**      如果
     useCache=true
     并且
     resultHandler=null*/
    
     

         
         
     if 
     (ms.isUseCache() && resultHandler == 
     null
     ) {
     

         
             
     ensureNoOutParams(ms
     , 
     parameterObject
     , 
     boundSql)
     ;
     

         
             
     @SuppressWarnings
     (
     "unchecked"
     )
     

         
             
     List<
     E
     > list = (List<
     E
     >) 
     tcm
     .getObject(cache
     , 
     key)
     ;
     

         
             
     if 
     (list == 
     null
     ) {
     

         
         
         
         
     /**      执行查询语句      */
    
     

         
         
         
         
     list = 
     delegate
     .<
     E
     >query(ms
     , 
     parameterObject
     , 
     rowBounds
     , 
     resultHandler
     , 
     key
     , 
     boundSql)
     ;
     

         
         
         
         
     /**      以
     cacheKey
     为主键,将结果维护到缓存中      */
    
     

         
         
         
         
     tcm
     .putObject(cache
     , 
     key
     , 
     list)
     ; 
     // issue #578 and #116
     

         
             
     }
     

         
             
     return 
     list
     ;
     

         
         
     }
     
    }
     
    return      delegate     .<     E     >query(ms
     , 
     parameterObject
     , 
     rowBounds
     , 
     resultHandler
     , 
     key
     , 
     boundSql)
     ;
     
}
     
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())
     ;
     
    // eg1: closed = false
     
    if      (     closed     ) {
     
        throw new      ExecutorException(     "Executor was closed."     )     ;
     
    }
          
    // eg1: queryStack = 0 ms.isFlushCacheRequired() = false
     

         /**      如果配置了
     flushCacheRequired=true
     并且
     queryStack=0
     (没有正在执行的查询操作),则会执行清空缓存操作
     */
    
     
    if      (     queryStack      ==      0 
     && ms.isFlushCacheRequired()) {
     
        clearLocalCache()     ;
     
    }
          
    List<     E     > list     ;
     
    try      {
     

             /**      记录正在执行查询操作的任务数
     */
    
     
        queryStack     ++     ;      // eg1: queryStack=1
          
        // eg1: resultHandler=null localCache.getObject(key)=null
     
                  /** localCache
     维护一级缓存,试图从一级缓存中获取结果数据,如果有数据,则返回结果;如果没有数据,再执行
     queryFromDatabase */
    
     
                  list = resultHandler ==      null      ? (List<
     E
     >) 
     localCache
     .getObject(key) : 
     null;
     
                  // eg1: list = 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()     ;
          

             /**      如果设置了
     <setting name="localCacheScope" value="STATEMENT"/>
     ,则会每次执行完清空缓存。即:使得一级缓存失效      */
    
     
                  if      (     configuration
     .getLocalCacheScope() == LocalCacheScope.
     STATEMENT
     ) {
     
                      // issue #482
     
                      clearLocalCache()     ;
     
                  }
     
    }
     
    return      list     ;
     
}


第四步:查询前的缓存处理, 判断是否需要从二级、一级缓存中取数据(先二级再一级) 。其时序图如下:



mybatis查询过程源码解析_源码解析_06


第五步:真正调用用jdbc调数据库,先预编译、再注入参数


触发时机:list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql) 


         -> doQuery(ms, parameter, rowBounds, resultHandler, boundSql)


走到这步,就是真的查询,过程为:先预编译、再注入参数、然后调用数据库查询 


接下来我们一步步进行解析:


@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()    ;
    
                        
    /**     根据
    Configuration
    来构建
    StatementHandler */
   
    
                           StatementHandler handler = configuration.newStatementHandler(    wrapper    ,     ms    , 
    parameter
    , 
    rowBounds
    , 
    resultHandler
    , 
    boundSql)
    ;
        
                          /**     然后使用
    prepareStatement
    方法,对
    SQL
    进行预编译并设置入参     */
   
    
                        stmt = prepareStatement(handler    ,     ms.getStatementLog())    ;
        
                                    /**     开始执行真正的查询操作。将包装好的
    Statement
    通过
    StatementHandler
    来执行,并把结果传递给
    resultHandler */
   
    
                                    return     handler.<
    E
    >query(stmt
    , 
    resultHandler)
    ;
    
    }     finally     {
    
                                    closeStatement(stmt)    ;
    
    }
    
}
    

    /**
   
    

    *     使用
    prepareStatement
    方法,对
    SQL
    编译并设置入参
   
    

    *
   
    

    *     @param     handler
   
    

    *     @param     statementLog
   
    

    *     @return
   
    

    *     @throws     SQLException
   
    

    */
   
    
private     Statement     prepareStatement    (StatementHandler handler    ,     Log statementLog) 
    throws 
    SQLException {
    
    Statement stmt    ;
        

        /**     获得
    Connection
    实例     */
   
    
    Connection connection = getConnection(statementLog)    ;
        

        /**     第一步:调用了
    StatementHandler
    的
    prepared
    进行了【
    sql
    的预编译】     */
   
    
    stmt = handler.prepare(connection    ,     transaction    .getTimeout())    ;
        

        /**     第二步:通过
    PreparedStatementHandler
    的
    parameterize
    来给【
    sql
    设置入参】     */
   
    
    handler.parameterize(stmt)    ;
        
        return     stmt    ;
    
}


第五步:真正调用用jdbc调数据库,先预编译、再注入参数 。其时序图如下:



mybatis查询过程源码解析_源码解析_07


第六步:进行结果集解析填充


这一步就属于结果集填充。可通过实现typeHandler来实现结果集填充的自定义填充功能。


入口源码如下:


/**
  
   

   *    处理结果集
  
   

   */
  
   
private void    handleResultSet(ResultSetWrapper rsw   , ResultMap resultMap   , List<Object> multipleResults   ,
   
throws
   
    try
   
        if    (parentMapping !=    null   ) {
   
, resultMap   , null, RowBounds.   DEFAULT
   , parentMapping)
   ;
   
else
   
            if    (   resultHandler    ==    null   ) {
   

                   /**    初始化
   ResultHandler
   实例,用于解析查询结果并存储于该实例对象中    */
  
   

       
       
       
       DefaultResultHandler defaultResultHandler =    new DefaultResultHandler(   objectFactory)   ;
   

       
       
       
       
   /**    解析行数据    */
  
   

       
       
       
       handleRowValues(rsw   , resultMap   , defaultResultHandler   ,    rowBounds   , null)   ;
   

       
       
       
       multipleResults.add(defaultResultHandler.getResultList())   ;
   

       
       
       }    else
   

       
       
       
       handleRowValues(rsw   , resultMap   ,    resultHandler   ,    rowBounds   , null)
   ;
   

       
       
       }
  
   

       
       }
  
   
finally
   

           /**    关闭
   ResultSet */
  
   
;
   

       }
  
   

   }


时序图如下:



mybatis查询过程源码解析_localCache_08


举报

相关推荐

0 条评论