myabtis源碼解析五(流式查詢)

mysql咱們都在用,可能有一半左右的公司可能都沒有接觸到數(shù)據(jù)量大了之后會出現(xiàn)的問題吧,因為業(yè)務(wù)就沒有那樣大,但是當(dāng)咱們數(shù)據(jù)庫里面數(shù)據(jù)量大了之后,去查詢大量數(shù)據(jù)
就會出現(xiàn)問題,什么問題那?原來,mysql會一次性的將數(shù)據(jù)查詢出來后放入內(nèi)存中,然后在返回到頁面,但是當(dāng)數(shù)據(jù)量巨大,這個時候就會撐爆咱們的內(nèi)存,導(dǎo)致OOM,我們公司
用戶量4000多萬,交易數(shù)據(jù)大概有幾十億條,這樣的數(shù)據(jù)量,如果查詢,秒級崩潰,咱們怎嘛解決那,一個比較常見的方式是使用分頁,那馬除了分頁,還有其他方法嗎?接下來,就
是這篇文章要討論的myabtis自帶的流式查詢,咱們一起來看一下
xml映射文件,就是一個簡單的查詢

<select id="selectTest" resultMap="base_result_map">
        select <include refid="base_column_list" /> from `ryx_account` where id BETWEEN 1 AND #{id}
    </select>

mapper

public List<RyxAccount> selectTest(@Param("id")int id);

controller

@RequestMapping("/findPage3")
    public String findPage() throws Exception {
        StopWatch stopWatch = new StopWatch("Test");
        stopWatch.start();
        sqlSessionTemplate.select("selectTest", 5000, resultContext -> {
            final RyxAccount ryxAccount = (RyxAccount) resultContext.getResultObject();
            System.out.println(Thread.currentThread().getName()+"線程"+JSON.toJSONString(ryxAccount));
        });
        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
        return "ok";
    }

啟動后咱們訪問后,可以看到

http-nio-8089-exec-2線程{"createTime":1561480357000,"id":1,"money":900,"name":"張三","updateTime":1561491458000}
http-nio-8089-exec-2線程{"createTime":1561480367000,"id":2,"money":1100,"name":"李四","updateTime":1561491458000}
http-nio-8089-exec-2線程{"createTime":1568732427000,"id":3,"money":0,"name":"_NAME0","updateTime":1568732427000}
http-nio-8089-exec-2線程{"createTime":1568732427000,"id":4,"money":0,"name":"_NAME0","updateTime":1568732427000}
http-nio-8089-exec-2線程{"createTime":1568732427000,"id":5,"money":1,"name":"_NAME1","updateTime":1568732427000}
http-nio-8089-exec-2線程{"createTime":1568732428000,"id":6,"money":0,"name":"_NAME0","updateTime":1568732428000}
http-nio-8089-exec-2線程{"createTime":1568732428000,"id":7,"money":1,"name":"_NAME1","updateTime":1568732428000}
http-nio-8089-exec-2線程{"createTime":1568732428000,"id":8,"money":2,"name":"_NAME2","updateTime":1568732428000}
http-nio-8089-exec-2線程{"createTime":1568732428000,"id":9,"money":0,"name":"_NAME0","updateTime":1568732428000}
http-nio-8089-exec-2線程{"createTime":1568732428000,"id":10,"money":1,"name":"_NAME1","updateTime":1568732428000}
http-nio-8089-exec-2線程{"createTime":1568732428000,"id":11,"money":2,"name":"_NAME2","updateTime":1568732428000}
http-nio-8089-exec-2線程{"createTime":1568732428000,"id":12,"money":3,"name":"_NAME3","updateTime":1568732428000}
http-nio-8089-exec-2線程{"createTime":1568732428000,"id":13,"money":0,"name":"_NAME0","updateTime":1568732428000}
http-nio-8089-exec-2線程{"createTime":1568732428000,"id":14,"money":1,"name":"_NAME1","updateTime":1568732428000}
http-nio-8089-exec-2線程{"createTime":1568732428000,"id":15,"money":2,"name":"_NAME2","updateTime":1568732428000}

會一條條的打印出結(jié)果,也就是會按照一條條的返回結(jié)果,這樣,就避免了查詢的數(shù)據(jù)量過大導(dǎo)致系統(tǒng)OOM
接下來我們分析下源碼,看看mybaits到底是怎樣實(shí)現(xiàn)的

demo很簡單,可以看到已經(jīng)實(shí)現(xiàn)了基本查詢,接下來,我們一起來看一下源碼,myabtis到底是怎樣實(shí)現(xiàn)的,再看流式查詢是如何實(shí)現(xiàn)之前,我們先來復(fù)習(xí)下前面的內(nèi)容
我們已經(jīng)知道sqSession有三個實(shí)現(xiàn)類
1:DefaultSqlSession,(sqlSession的默認(rèn)實(shí)現(xiàn),非線程安全)
2:SqlSessionManager,(sqlSession管理器,通過ThreadLocal實(shí)現(xiàn)線程安全的sqlSession)
3:SqlSessionTemplete(spring整合myabtis提供的SqlSession模板,由于現(xiàn)在基本上咱們在用mybatis的時候,都不會單獨(dú)的只用mybais框架,都會引入spring框架,所以我認(rèn)為
此類就是為了mybatis整合spring提供的專門的模板類)

那大家又要問了.DefaultSqlSession為什么線程不安全,我們再來復(fù)習(xí)下前面的sql語句的執(zhí)行過程,
1:確定當(dāng)前執(zhí)行的sql語句是查詢還是插入或者是更新(查詢?yōu)槔?
2:調(diào)用執(zhí)行器的querry方法,myabtis默認(rèn)的是 SimpleExecutor執(zhí)行器
3:SimpleExecutor執(zhí)行器執(zhí)行的時候會調(diào)用baseExecuter
4:調(diào)用queryFromDatabase方法查詢數(shù)據(jù)庫,如果命中緩存,直接從緩存中返回數(shù)據(jù),如果未命中,查詢數(shù)據(jù)庫,并放入緩存
線程不安全就是出現(xiàn)在緩存這一塊baseExecuter中定義的localCache是hashMap,hashMap就是線程不安全的,試想一個過程
線程A查詢數(shù)據(jù)庫,假設(shè)查詢出了10條數(shù)據(jù),就在將要放入緩存的時候,線程B進(jìn)來了,然后這個時候,插入了一條數(shù)據(jù),查詢出了11條數(shù)據(jù)
更新了緩存,然后線程A這個時候再去更新緩存,將數(shù)據(jù)重新更新為10條,這樣就導(dǎo)致了后續(xù)的查詢都使用的是線程1的緩存容为,導(dǎo)致查詢結(jié)果不正確梧却。
那嗎myabtis是如何解決這個問題的那
接下來就是SqlSessionManager和SqlSessionTemplete出場了
咱們先來分析SqlSessionTemplete這個myabtis為spring專門生成的模板類

#SqlSessionTemplate
public class SqlSessionTemplate implements SqlSession, DisposableBean {

    //session工廠
  private final SqlSessionFactory sqlSessionFactory;

    //執(zhí)行器類型
  private final ExecutorType executorType;

    //sqlSession
  private final SqlSession sqlSessionProxy;

    //異常轉(zhuǎn)換器
  private final PersistenceExceptionTranslator exceptionTranslator;

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
  }

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
    this(sqlSessionFactory, executorType,
        new MyBatisExceptionTranslator(
            sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
  }

    //構(gòu)造函數(shù)
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    //1.使用代理的方式獲取sqlSession的代理類實(shí)例
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        //創(chuàng)建SqlSessionInterceptor類實(shí)現(xiàn)InvocationHandler 接口,這是處理selSesison的核心
        new SqlSessionInterceptor());
  }

  //...省略部分方法

 //2需要將MyBatis方法調(diào)用路由到從Spring的事務(wù)管理器獲得的適當(dāng)SqlSession的代理*還可以將{@code Method#invoke(Object置谦,Object ...)}
   //引發(fā)的異常解包以*將{@code PersistenceException}傳遞給{@code PersistenceExceptionTranslator}蚤认。
  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      //1:獲取SqlSession這個方法可以根據(jù)Spring的事物上下文來獲取事物范圍內(nèi)的sqlSession
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
         //2:調(diào)用從Spring的事物上下文獲取事物范圍內(nèi)的sqlSession對象
        Object result = method.invoke(sqlSession, args);
         //校驗當(dāng)前的sqlSession是否被Spring管理 如果未被Spring托管則自動commit
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        //如果出現(xiàn)異常則根據(jù)情況轉(zhuǎn)換后拋出
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        //3:關(guān)閉sqlSession
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

}

首先當(dāng)我們啟動項目的時候,會加載sqlSessionTemplete的構(gòu)造函數(shù),為sqlSession生成代理類


image.png

接下來重點(diǎn)分析方法1和方法3
1:getSqlSession
3:closeSqlSession

getSqlSession

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory) {
//獲取執(zhí)行器類型
ExecutorType executorType = sessionFactory.getConfiguration().getDefaultExecutorType();
//調(diào)用重載方法
return getSqlSession(sessionFactory, executorType, null);
}

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

 //獲取sqlSession的持有者對象
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

//從SqlSessionHolder對象中獲取SqlSession對象
SqlSession session = sessionHolder(executorType, holder);
//不為null直接返回session,為null的話,執(zhí)行下面邏輯
if (session != null) {
  return session;
}

LOGGER.debug(() -> "Creating a new SqlSession");
//如果當(dāng)前事物管理器中獲取不到SqlSessionHolder對象就重新創(chuàng)建一個
session = sessionFactory.openSession(executorType);
//將新創(chuàng)建的SqlSessionHolder對象注冊到TransactionSynchronizationManager中
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

return session;

}

上面方法中出現(xiàn)了一個新的類TransactionSynchronizationManager(事物同步管理器類),大膽猜想sqlSession的線程安全就應(yīng)該在這里啦,我們?nèi)タ纯催@個類

TransactionSynchronizationManager

//獲取綁定到當(dāng)前線程的對應(yīng)的鍵的資源
public static Object getResource(Object key) {
    //對包資源執(zhí)行解析
    Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
    //獲取資源
    Object value = doGetResource(actualKey);
    if (value != null && logger.isTraceEnabled()) {
        logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
                Thread.currentThread().getName() + "]");
    }
    return value;
}

doGetResource

private static Object doGetResource(Object actualKey) {
//從resources中獲取map集合,resources定義為一個threadLocal里面的對象是一個map
Map<Object, Object> map = resources.get();
//如果map為null直接返回
if (map == null) {
return null;
}
//獲取資源對應(yīng)的值,這里的map中存儲的key和value到底是什么那
Object value = map.get(actualKey);
// 刪除無效的資源持有者
if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
map.remove(actualKey);
// Remove entire ThreadLocal if empty...
if (map.isEmpty()) {
resources.remove();
}
value = null;
}
return value;
}
這里咱們有個問題,resources這個ThreadLocal里面的map中的key和value到底存儲的是什么那,繼續(xù)往下看

registerSessionHolder

private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
SqlSessionHolder holder;
//判斷當(dāng)前的ThreadLocal中是否存在對應(yīng)的TransactionSynchronization
if (TransactionSynchronizationManager.isSynchronizationActive()) {
//從sessionFactory對象中獲取Environment對象
Environment environment = sessionFactory.getConfiguration().getEnvironment();

   //判斷事物工廠是不是有spring事物管理
  if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
    LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]");
    //構(gòu)建sqlSession持有者對象
    holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
    //將指定鍵的指定資源綁定到當(dāng)前線程
    TransactionSynchronizationManager.bindResource(sessionFactory, holder);
    //為當(dāng)前線程注冊新的事物
    TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
    //設(shè)置事物同步
    holder.setSynchronizedWithTransaction(true);
    //注冊成功后,表明有人拿到了資源,就將引用計數(shù)執(zhí)行+1操作
    holder.requested();
  } else {
    //從datasource獲取資源,如果為null,證明是非事物的,否則拋出異常
    if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
      LOGGER.debug(() -> "SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
    } else {
      throw new TransientDataAccessResourceException(
          "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
    }
  }
} else {
  LOGGER.debug(() -> "SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
}

}

這里有5個步驟,其中2和3步驟我們具體分析下,其他步驟就不做具體分析了
1:構(gòu)建sqlSession持有者對象
2:將指定鍵的指定資源綁定到當(dāng)前線程
3:為當(dāng)前線程注冊新的事物
4:設(shè)置事物同步
5:執(zhí)行引用計數(shù)+1

bindResource

public static void bindResource(Object key, Object value) throws IllegalStateException {
//執(zhí)行資源解析
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Assert.notNull(value, "Value must not be null");
//從resources獲取map集合(resources是一個ThreadLocal對象)
Map<Object, Object> map = resources.get();
// 向threadLocal中設(shè)置空map集合
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
//將sessionFactory作為key,SqlSessionHolder作為value放入map集合中
Object oldValue = map.put(actualKey, value);
// 如果sqlSession持有者已經(jīng)存在,則設(shè)置為null
if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
oldValue = null;
}
//不為null,則拋出異常,已經(jīng)存在sqlSesison
if (oldValue != null) {
throw new IllegalStateException("Already value [" + oldValue + "] for key [" +
actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
}
if (logger.isTraceEnabled()) {
logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +
Thread.currentThread().getName() + "]");
}
}

registerSynchronization

public static void registerSynchronization(TransactionSynchronization synchronization)
throws IllegalStateException {

    //判斷TransactionSynchronization是否為null
    Assert.notNull(synchronization, "TransactionSynchronization must not be null");
    if (!isSynchronizationActive()) {
        throw new IllegalStateException("Transaction synchronization is not active");
    }
    //將synchronization添加到ThreadLocal對象的set集合中
    synchronizations.get().add(synchronization);
}

closeSqlSession
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
notNull(session, NO_SQL_SESSION_SPECIFIED);
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);

//獲取sqlSession的持有者
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
//判斷sqlSession的持有者是否為null
if ((holder != null) && (holder.getSqlSession() == session)) {
  LOGGER.debug(() -> "Releasing transactional SqlSession [" + session + "]");
   //將sqlSession持有者的引用計數(shù)減一
  holder.released();
} else {
//如果不是被spring管理,那么就不會被Spring去關(guān)閉回收,就需要自己close
  LOGGER.debug(() -> "Closing non transactional SqlSession [" + session + "]");
  session.close();
}

}
寫到這里,會不會感覺有點(diǎn)多余,不是流式查詢嗎,怎嘛分析到這里了,總的來說,流式查詢的代碼很簡單,咱們可以直接使用,但是還是想在分析流式查詢之前再去分析下這幾個包裝類
我這里做個大膽的猜想:
mybatis提供的defaultSqlSesison線程不安全,所以引入了sqlSessionManager來使得sqlSession線程安全,但是現(xiàn)在我們在使用mybaits的時候,都是配合spring一起使用的
所以出現(xiàn)了一個為spring分裝的sqlSessionTemplete使用ThreadLocal的方式使得sqlSession線程安全,這樣的話,sqlSessionManager這個類就不會被使用了,不知道猜測對不對,

接下來,還有一個實(shí)現(xiàn)了sqlSession的類SqlSessionManager,我們在一起分析下

public class SqlSessionManager implements SqlSessionFactory, SqlSession {

private final SqlSessionFactory sqlSessionFactory;
private final SqlSession sqlSessionProxy;

private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();

private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
//使用代理的方式獲取sqlSession
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor());
}

//省略部分代碼....

private class SqlSessionInterceptor implements InvocationHandler {
public SqlSessionInterceptor() {
// Prevent Synthetic Access
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  //從threadlocal獲取當(dāng)前線程對應(yīng)的sqlSession
  final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
    //執(zhí)行代理方法最終執(zhí)行sql語句
  if (sqlSession != null) {
    try {
      return method.invoke(sqlSession, args);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  } else {
    //如果當(dāng)前線程中沒有sqlSession,就調(diào)用SqlSessionFactory創(chuàng)建一個sqlSession
    final SqlSession autoSqlSession = openSession();
    try {
      //執(zhí)行sql語句
      final Object result = method.invoke(autoSqlSession, args);
      //執(zhí)行事務(wù)提交
      autoSqlSession.commit();
      return result;
    } catch (Throwable t) {
      //出現(xiàn)異常執(zhí)行回滾
      autoSqlSession.rollback();
      throw ExceptionUtil.unwrapThrowable(t);
    } finally {
      autoSqlSession.close();
    }
  }
}

}

}

以上就是我對defaultSqlSession,sqlSessionManager,sqlSessionTemplete的一點(diǎn)分析,接下來,我們進(jìn)入到今天的主題,流式查詢的源碼分析中
由于我的項目是引入了spring框架,所以當(dāng)我執(zhí)行sql語句的時候,會被sqlSessionTemplete的攔截器攔截,執(zhí)行sqlSession的獲取操作,
接下來,我們進(jìn)入sqlSessionTemplete的流式查詢的select方法
我們看到存在三個重載方法
String statement, 執(zhí)行的sql語句的mapper映射方法
Object parameter,參數(shù)對象
RowBounds rowBounds,分頁
ResultHandler handler結(jié)果處理器

@Override
public void select(String statement, ResultHandler handler) {
this.sqlSessionProxy.select(statement, handler);
}

/**

  • {@inheritDoc}
    */
    @Override
    public void select(String statement, Object parameter, ResultHandler handler) {
    this.sqlSessionProxy.select(statement, parameter, handler);
    }

/**

  • {@inheritDoc}
    */
    @Override
    public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
    }

//接著往下分析,看一看mybatis的流式查詢到底是怎嘛實(shí)現(xiàn)的,我們執(zhí)行下以下方法,
1:首先被sqlSessionTemplete的攔截器攔截,獲取線程安全的sqlSession
sqlSessionTemplate.select("selectTest", 5000, resultContext -> {
final RyxAccount ryxAccount = (RyxAccount) resultContext.getResultObject();
System.out.println(Thread.currentThread().getName()+"線程"+JSON.toJSONString(ryxAccount));
});

2:執(zhí)行以下重載方法
@Override
public void select(String statement, Object parameter, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, handler);
}

3:按照之前的分析,mybatis的默認(rèn)實(shí)現(xiàn)是DefaultsqlSession,進(jìn)入DefaultsqlSession的select方法
@Override
public void select(String statement, Object parameter, ResultHandler handler) {
//進(jìn)入重載方法
select(statement, parameter, RowBounds.DEFAULT, handler);
}

@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
//獲取statement對象
MappedStatement ms = configuration.getMappedStatement(statement);
//執(zhí)行數(shù)據(jù)庫查詢操作
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
4:進(jìn)入CacheExecuter的querry方法
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//獲取sql語句
BoundSql boundSql = ms.getBoundSql(parameterObject);
//創(chuàng)建緩存
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
//調(diào)用querry方法
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
//查看緩存是否為空
if (cache != null) {
flushCacheIfRequired(ms);
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);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
5:進(jìn)入BaseExecuter的querry方法
@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.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
//獲取結(jié)果集
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
//判斷是否在緩存中
if (list != null) {
//處理本地緩存的輸出參數(shù)
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//查詢數(shù)據(jù)庫
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(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
//向緩存中添加標(biāo)志位
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//執(zhí)行默認(rèn)執(zhí)行器的doQuerry方法
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;
}
6:進(jìn)入SimpleExecuter的doQuerry方法
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
7:進(jìn)入PreparedStatementHandler的Querry方法
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
8:進(jìn)入DefaultResultSetHandler的handleResultSets方法
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

final List<Object> multipleResults = new ArrayList<Object>();

int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);

List<ResultMap> resultMaps = mappedStatement.getResultMaps();
//結(jié)果映射數(shù)
int resultMapCount = resultMaps.size();
//檢查結(jié)果映射數(shù)量
validateResultMapsCount(rsw, resultMapCount);

while (rsw != null && resultMapCount > resultSetCount) {
 //獲取結(jié)果映射
  ResultMap resultMap = resultMaps.get(resultSetCount);
  //處理映射結(jié)果集
  handleResultSet(rsw, resultMap, multipleResults, null);
  rsw = getNextResultSet(stmt);
  cleanUpAfterHandlingResultSet();
  //結(jié)果集數(shù)量加1,
  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);

}

//處理結(jié)果集
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
//處理父映射
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
//處理行值
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
//處理嵌套結(jié)果集映射
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
//處理簡單結(jié)果集映射
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
//處理簡單結(jié)果集的行值
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
skipRows(rsw.getResultSet(), rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
Object rowValue = getRowValue(rsw, discriminatedResultMap);
//存儲對象
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
//存儲對象方法
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
if (parentMapping != null) {
linkToParents(rs, parentMapping, rowValue);
} else {
//調(diào)用結(jié)果處理程序
callResultHandler(resultHandler, resultContext, rowValue);
}
}
//調(diào)用結(jié)果處理方法
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
resultContext.nextResultObject(rowValue);
((ResultHandler<Object>) resultHandler).handleResult(resultContext);
}
9:進(jìn)入DefaultResultContext的nextResultObject方法
public void nextResultObject(T resultObject) {
//行值+1原來最終的處理在這里,真的踏破鐵鞋無覓處得來全不費(fèi)工夫
resultCount++;
//返回結(jié)果對象
this.resultObject = resultObject;
}

myabtis的流式查詢就弄完了,啰啰嗦嗦的說了好多,會有很多不足的地方,萬望指教,下一期我們分析下mybatis的游標(biāo)查詢
Thanks
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市捧请,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棒搜,老刑警劉巖疹蛉,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異力麸,居然都是意外死亡可款,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進(jìn)店門克蚂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闺鲸,“玉大人,你說我怎么就攤上這事埃叭∶校” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵赤屋,是天一觀的道長立镶。 經(jīng)常有香客問我,道長类早,這世上最難降的妖魔是什么媚媒? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮莺奔,結(jié)果婚禮上欣范,老公的妹妹穿的比我還像新娘。我一直安慰自己令哟,他們只是感情好恼琼,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著屏富,像睡著了一般晴竞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狠半,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天噩死,我揣著相機(jī)與錄音,去河邊找鬼神年。 笑死已维,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的已日。 我是一名探鬼主播垛耳,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了堂鲜?” 一聲冷哼從身側(cè)響起栈雳,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缔莲,沒想到半個月后哥纫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡痴奏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年蛀骇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抛虫。...
    茶點(diǎn)故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡松靡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出建椰,到底是詐尸還是另有隱情雕欺,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布棉姐,位于F島的核電站屠列,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏伞矩。R本人自食惡果不足惜笛洛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望乃坤。 院中可真熱鬧苛让,春花似錦、人聲如沸湿诊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厅须。三九已至仿畸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間朗和,已是汗流浹背错沽。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留眶拉,地道東北人千埃。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像忆植,于是被迫代替她去往敵國和親镰禾。 傳聞我的和親對象是個殘疾皇子皿曲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容