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生成代理類
接下來重點(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