【源碼篇】聊聊源碼mybatis(查詢源碼分析)

1、舉個case-根據主鍵ID查詢單個實體對象

下面我們分析一個最常用的mapper接口,根據主鍵ID查詢單個實體對象的源碼實現邏輯。

省略配置文件…因爲重點在於分析查詢源碼,先回顧下我們應用app服務端開發熟悉的配置mybatis過程。

//加載MapperConfig.xml的配置文件
final String resource = "org/apache/ibatis/builder/MapperConfig.xml";
//根據配置文件獲取字符流
    final Reader reader = Resources.getResourceAsReader(resource);
//根據字符流構建SqlSessionFactory
  SqlSessionFactory  sqlMapper = new SqlSessionFactoryBuilder().build(reader);
//獲取全局核心配置類
    Configuration configuration = new Configuration(sqlMapper.getConfiguration().getEnvironment());
    //爲全局配置添加需要的Mapper接口
    configuration.addMapper(AuthorMapperWithAnnotation.class);
    //創建SQL會話工廠
    SqlSessionFactory sqlMapperWithAnnotation = new DefaultSqlSessionFactory(configuration);
    //獲取SQL會話
    SqlSession sqlSession =  sqlMapperWithAnnotation.openSession();
    //從會話中獲取綁定好的mapper接口信息
    AuthorMapperWithAnnotation mapper = sqlSession.getMapper(AuthorMapperWithAnnotation.class);
    //執行mapper接口的實現方法
    Author author = mapper.selectAuthorInfoById(101);
    //期待返回正確的數據
    assertEquals(101,author.getId());

通常我們會定義mapper接口文件或者通過註解方式來實現,這裏舉例註解方式。

/**
 * @author liuxiaocheng
 * @create 2020/3/28  9:43 上午
 */
public interface AuthorMapperWithAnnotation {

  // TODO: 2020/3/28 by wolf_love666 for study source code
  @Select("select id, username, password, email, bio, favourite_section from author where id = #{id}")
  Author selectAuthorInfoById(int id);
}

通常意義上我們的服務端開發已經結束了,然後會進行單元測試看下數據是否正常返回。而今天我們將揭開mybatis是如何返回正確的結果的。

這是完整的mybatis執行時序圖。
在這裏插入圖片描述

2、核心類和核心方法

2.1、Part1​【解析】⚡️解析Mapper接口、@Select註解和id入參。

MapperProxy(根據動態代理執行目標mapper接口)

MapperMethod(根據@Select找到對應的Select方法、根據方法簽名確定返回參數)

ParamNameResolver(根據args入參解析屬性入參,獲取對應的動態值)

2.1.1、MapperProxy源碼分析

在這裏插入圖片描述

/**
 * Mapper接口代理--真正的mapper執行類
 */
public class MapperProxy<T> implements InvocationHandler, Serializable {
 
   ...省略本次無關內容...

  //mapper接口的執行
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      //object類直接執行
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
        //緩存的執行器執行
      } else {
        return cachedInvoker(proxy, method, args).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
  //緩存的執行器
  private MapperMethodInvoker cachedInvoker(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      return methodCache.computeIfAbsent(method, m -> {
        if (m.isDefault()) {
          try {
            //jdk8和jdk9的默認方法執行
            if (privateLookupInMethod == null) {
              return new DefaultMethodInvoker(getMethodHandleJava8(method));
            } else {
              return new DefaultMethodInvoker(getMethodHandleJava9(method));
            }
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException
              | NoSuchMethodException e) {
            throw new RuntimeException(e);
          }
        } else {
          //返回聲明的方法執行器
          return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
  }
 //Java9獲取方法處理器
  private MethodHandle getMethodHandleJava9(Method method)
      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    final Class<?> declaringClass = method.getDeclaringClass();
    return ((Lookup) privateLookupInMethod.invoke(null, declaringClass, MethodHandles.lookup())).findSpecial(
        declaringClass, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()),
        declaringClass);
  }
 //Java8獲取方法處理器
  private MethodHandle getMethodHandleJava8(Method method)
      throws IllegalAccessException, InstantiationException, InvocationTargetException {
    final Class<?> declaringClass = method.getDeclaringClass();
    return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass);
  }
  //Mapper方法執行器接口
  interface MapperMethodInvoker {
    Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
  }
  ...省略本次無關內容...
}


2.1.2、MapperMethod源碼分析

在這裏插入圖片描述

/**
 * Mapper的方法 add by wolf_love666
 */
public class MapperMethod {
  //命令
  private final SqlCommand command;
  //方法簽名
  private final MethodSignature method;
  //映射的接口和方法和全局配置
  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }
  //執行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);
          //返回map
        } 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);
          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());
    }
    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;
  }
}

2.1.3、ParamNameResolver源碼分析

在這裏插入圖片描述

/**
 * 參數名字解析器 add by wolf_love666
 */
public class ParamNameResolver {

  public static final String GENERIC_NAME_PREFIX = "param";
 
 ...省略本次無關內容...
   
  /**
   * 
   返回一個沒有名稱的非特殊參數。 使用命名規則來命名多個參數。 除了默認名稱外,此方法還添加了通用名稱(param1,param2,* ...)。
   */
  public Object getNamedParams(Object[] args) {
    //獲取對象
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];
    } else {
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        param.put(entry.getValue(), args[entry.getKey()]);
        //添加通用的參數名字
        final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
        // 確保帶有@Param註解參數的不被重寫
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }
}

2.2、Part2【執行】⚡️SqlSession的會話處理,獲取映射會話,執行查詢

  • DefaultSqlSession(主要完成從全局配置獲取會話,和執行綁定的查詢)
  • Configuration(全局配置-來源於加載全局配置文件的時候初始化的信息,也就是說如果你的mapper接口是在啓動服務之後新加的,那麼這裏找不到則會報錯。)
  • CachingExecutor(默認開啓的是session會話緩存,也就是一級緩存,所以會走緩存執行器,如果是Object類的話則直接處理屬於特殊情況)
  • MappedStatement(映射綁定的會話)
  • BoundSql(綁定傳入的sql)
  • BaseExecutor(抽象的公共執行器)
  • SimpleExecutor(簡單的執行器執行)
  • RoutingStatementHandler和PreparedStatementHandler會話處理器

2.2.1、DefaultSqlSession源碼分析

在這裏插入圖片描述
在這裏插入圖片描述

/**
 * 默認的會話實現類,這個類非安全 add by wolf_love666
 */
public class DefaultSqlSession implements SqlSession {
  //全局配置
  private final Configuration configuration;
  //執行器
  private final Executor executor;
   ...省略本次無關內容...
//查詢單條數據
  @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.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;
    }
  } 
@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 {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
   ...省略本次無關內容...
}

2.2.2、Configuration源碼分析

在這裏插入圖片描述

/**
*全局配置類 add by wolf_love666
*/
public class Configuration { 
  
     ...省略本次無關內容...

  //獲取映射的會話根據會話ID
public MappedStatement getMappedStatement(String id) {
    return this.getMappedStatement(id, true);
  }
  //獲取映射的會話根據會話ID
  public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
    //是否需要驗證不匹配的會話
    if (validateIncompleteStatements) {
      //構建所有會話驗證
      buildAllStatements();
    }
    //獲取映射的會話根據會話ID
    return mappedStatements.get(id);
  }
 /*
   * 解析緩存中所有未處理的語句節點。建議一旦添加了所有映射器,便調用此方法,因爲它提供了快速失敗語句驗證
   */
  protected void buildAllStatements() {
    parsePendingResultMaps();
    if (!incompleteCacheRefs.isEmpty()) {
      synchronized (incompleteCacheRefs) {
        incompleteCacheRefs.removeIf(x -> x.resolveCacheRef() != null);
      }
    }
    if (!incompleteStatements.isEmpty()) {
      synchronized (incompleteStatements) {
        incompleteStatements.removeIf(x -> {
          x.parseStatementNode();
          return true;
        });
      }
    }
    if (!incompleteMethods.isEmpty()) {
      synchronized (incompleteMethods) {
        incompleteMethods.removeIf(x -> {
          x.resolve();
          return true;
        });
      }
    }
  }
  //解析等待的結果map集
  private void parsePendingResultMaps() {
    if (incompleteResultMaps.isEmpty()) {
      return;
    }
    synchronized (incompleteResultMaps) {
      boolean resolved;
      IncompleteElementException ex = null;
      do {
        resolved = false;
        Iterator<ResultMapResolver> iterator = incompleteResultMaps.iterator();
        while (iterator.hasNext()) {
          try {
            iterator.next().resolve();
            iterator.remove();
            resolved = true;
          } catch (IncompleteElementException e) {
            ex = e;
          }
        }
      } while (resolved);
      if (!incompleteResultMaps.isEmpty() && ex != null) {
        // At least one result map is unresolvable.
        throw ex;
      }
    }
  }
     ...省略本次無關內容...

}

在這裏插入圖片描述
Configuration中的內部類StrictMap

//嚴格的map內部實現類
  protected static class StrictMap<V> extends HashMap<String, V> {
   ...省略本次無關內容...
   //獲取全局配置的屬性對應的值
    @Override
    public V get(Object key) {
      V value = super.get(key);
      if (value == null) {
        throw new IllegalArgumentException(name + " does not contain value for " + key);
      }
      if (value instanceof Ambiguity) {
        throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
            + " (try using the full name including the namespace, or rename one of the entries)");
      }
      return value;
    }
  //內部語義不明的類
    protected static class Ambiguity {
      final private String subject;
       public Ambiguity(String subject) {
    this.subject = subject;
  }

  public String getSubject() {
    return subject;
  }
}

private String getShortName(String key) {
  final String[] keyParts = key.split("\\.");
  return keyParts[keyParts.length - 1];
}}

2.2.3、CachingExecutor源碼分析

/**
*緩存執行器
*/
public class CachingExecutor implements Executor {
  //代表執行器
  private final Executor delegate;
  
     ...省略本次無關內容...
       
  //查詢,並創建緩存
  @Override
  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);
    //執行綁定SQL後和緩存key查詢
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
  
  //帶緩存key查詢
  @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) {
          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);
  }
}

當前數據值
在這裏插入圖片描述
MappedStatement ms參數需要關注的值
在這裏插入圖片描述
在這裏插入圖片描述

2.2.4、MappedStatement源碼分析

/**
* 被映射的會話
*/
public final class MappedStatement {
       ...省略本次無關內容...

  //獲取綁定的SQL
public BoundSql getBoundSql(Object parameterObject) {
  //1、根據上圖可以得知sqlSource中的sql爲:
  //select id, username, password, email, bio, favourite_section from author where id = ?
  //id入參數類型爲int,
  //2、將參數 動態數據值101 和 靜態的SQL綁定,getBoundSql實際上調用的是BoundSql的構造函數BoundSql()。可參考下面BoundSql源碼構造函數
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
 //獲取參數映射集合實際上就是動態參數傳入的時候方法的入參數屬性,這裏就是int類型的ID
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
 //如果無參數映射傳入,則重新構造綁定SQL根據數據池的全局配置configuration        
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // 檢查參數映射中是否有嵌套情況
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }
       ...省略本次無關內容...

  }

2.2.5、BoundSql源碼分析

public class BoundSql {
  //sql
  private final String sql;
  //參數映射
  private final List<ParameterMapping> parameterMappings;
  //參數對象
  private final Object parameterObject;
  //附加參數
  private final Map<String, Object> additionalParameters;
  //元參數
  private final MetaObject metaParameters;
//綁定SQL構造器
  public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.parameterObject = parameterObject;
    this.additionalParameters = new HashMap<>();
    this.metaParameters = configuration.newMetaObject(additionalParameters);
  }
       ...省略本次無關內容...

  }

2.2.6、BaseExecutor源碼分析

/**
 * 基礎執行器 add by wolf_love666
 */
public abstract class BaseExecutor implements Executor {
   //日誌
  private static final Log log = LogFactory.getLog(BaseExecutor.class);
  //事務
  protected Transaction transaction;
  //執行器包裝器
  protected Executor wrapper;
  //併發隊列,延遲加載
  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  //永久的本地緩存
  protected PerpetualCache localCache;
  //永久的本地外放參數緩存
  protected PerpetualCache localOutputParameterCache;
  //配置對象--全局很重要
  protected Configuration configuration;
  //查詢棧
  protected int queryStack;
  //是否關閉
  private boolean closed;
  
         ...省略本次無關內容...

//帶緩存查詢會話
  @SuppressWarnings("unchecked")
  @Override
  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.");
    }
    //查詢棧=0並且要求被刷新緩存的會話操作
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      //清除本地緩存
      clearLocalCache();
    }
    //查詢棧+1
    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 {
      //查詢棧-1維護初始0
      queryStack--;
    }
    //如果查詢棧=0則延遲加載隊列 進行延遲循環加載
    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(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 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);
    //如果會話類型是回調,則本地外放參數緩存存放
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
           ...省略本次無關內容...
}

2.2.7、SimpleExecutor源碼分析

 
 /**
 * 簡單執行器 add by wolf_love666
 */
public class SimpleExecutor extends BaseExecutor {
             ...省略本次無關內容...

//執行查詢
  @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.query(stmt, resultHandler);
    } finally {
      //關閉會話
      closeStatement(stmt);
    }
  }
    //預準備會話
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //從BaseExecutor中獲取事務管理鏈接和會話日誌
    Connection connection = getConnection(statementLog);
    //給會話綁定攔截器,可以跳過
    stmt = handler.prepare(connection, transaction.getTimeout());
    //攔截器的參數處理,可以跳過
    handler.parameterize(stmt);
    return stmt;
  }
             ...省略本次無關內容...

}

2.2.8、RoutingStatementHandler和PreparedStatementHandler源碼分析

/**
 * 路由會話處理器
 * @author Clinton Begin
 */
public class RoutingStatementHandler implements StatementHandler {
  //會話處理器
  private final StatementHandler delegate;

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    switch (ms.getStatementType()) {
      //會話操作
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
        //預會話操作
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        //回調會話操作
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }
                 ...省略本次無關內容...

//查詢
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.query(statement, resultHandler);
  }
  }
/**
 * 預會話處理器 add by wolf_love666
 */
public class PreparedStatementHandler extends BaseStatementHandler {
                   ...省略本次無關內容...

//查詢
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //執行jdbc操作 參考2.4操作數據庫                
    ps.execute();
    //處理返回的結果集 參考2.5結果集處理
    return resultSetHandler.handleResultSets(ps);
  }
                   ...省略本次無關內容...

  }

2.4、Part3【操作數據庫】⚡️jdbc數據操作

操作數據庫階段主要完成會話執行SQL操作–不是本次重點跳過

當然數據庫這裏可以有多種MySQL,Oracle,MongoDB等等

2.5、Part4​【結果集處理】⚡️結果集處理封裝

2.5.1、DefaultResultSetHandler源碼分析

/**
 * 默認結果集處理器 add by wolf_love666
 */
public class DefaultResultSetHandler implements ResultSetHandler {
  //延遲對象
  private static final Object DEFERRED = new Object();
  //執行器
  private final Executor executor;
  //全局配置
  private final Configuration configuration;
  //映射會話
  private final MappedStatement mappedStatement;
  //行數據
  private final RowBounds rowBounds;
  //參數處理器
  private final ParameterHandler parameterHandler;
  //結果處理器
  private final ResultHandler<?> resultHandler;
  //執行SQL
  private final BoundSql boundSql;
  //類型處理器註冊器
  private final TypeHandlerRegistry typeHandlerRegistry;
  //對象工廠
  private final ObjectFactory objectFactory;
  //反射工廠
  private final ReflectorFactory reflectorFactory;
  //嵌套結果映射
  private final Map<CacheKey, Object> nestedResultObjects = new HashMap<>();
  //原型對象
  private final Map<String, Object> ancestorObjects = new HashMap<>();
  //先前行值
  private Object previousRowValue;
  //多個結果集
  private final Map<String, ResultMapping> nextResultMaps = new HashMap<>();
  //等待的關係
  private final Map<CacheKey, List<PendingRelation>> pendingRelations = new HashMap<>();
   //自動映射的緩存
  private final Map<String, List<UnMappedColumnAutoMapping>> autoMappingsCache = new HashMap<>();
  //暫時標記 使用構造器映射(使用屬性減少內存使用)
  private boolean useConstructorMappings;
  //等待關係
  private static class PendingRelation {
    public MetaObject metaObject;
    public ResultMapping propertyMapping;
  }
  //未映射的列自動映射  內部類
  private static class UnMappedColumnAutoMapping {
    private final String column;
    private final String property;
    private final TypeHandler<?> typeHandler;
    private final boolean primitive;

    public UnMappedColumnAutoMapping(String column, String property, TypeHandler<?> typeHandler, boolean primitive) {
      this.column = column;
      this.property = property;
      this.typeHandler = typeHandler;
      this.primitive = primitive;
    }
  }
                   ...省略本次無關內容...


//處理結果集
  // HANDLE RESULT SETS
  //
  @Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    //錯誤日誌上下文
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
    //多個返回結果集
    final List<Object> multipleResults = new ArrayList<>();
    //結果集數量
    int resultSetCount = 0;
    //獲取第一個會話的結果集修飾器
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    //獲取會話的結果映射
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    //結果映射數量                 
    int resultMapCount = resultMaps.size();
    //校驗結果映射數量是否一致否則拋出異常                 
    validateResultMapsCount(rsw, resultMapCount);
    //處理結果集                 
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
    //獲取結果集
    String[] resultSets = mappedStatement.getResultSets();
    //結果集不爲空                 
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        //獲取指定的結果集數量的結果映射
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        //處理嵌套映射情況-比如有內部類或者級聯查詢
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        //處理結果集之後情空
        cleanUpAfterHandlingResultSet();
        //結果集數量累計
        resultSetCount++;
      }
    }
    //合成同一個結果集
    return collapseSingleResultList(multipleResults);
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章