Mybatis自帶是使用SqlSession進(jìn)行數(shù)據(jù)的增刪該查操作愉适,支持一級(jí)緩存啸盏,存儲(chǔ)作用域?yàn)橥粋€(gè)SqlSession什湘,當(dāng)Session flush或close之后,該Session中的所有Cache就將清空赎离。二級(jí)緩存建議采用業(yè)務(wù)處理逛犹。因?yàn)閙ybatis支持的二級(jí)緩存是存儲(chǔ)作用域?yàn)镸apper(Namespace),可自定義存儲(chǔ)源,如Ehcache虽画,Redis掠手。
1. SqlSession入口
sqlSessionFactory在上一篇仔細(xì)分析了:Mybatis源碼第一篇:sqlSessionFactory創(chuàng)建流程. 下面以查詢語句分析sqlsession
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("user.selectOne",account);
SqlSession是執(zhí)行增刪改查的接口,默認(rèn)實(shí)現(xiàn)類是:DefaultSqlSession
,主要這個(gè)類是非線程安全狸捕。非線程安全是指多線程操作同一個(gè)對(duì)象可能會(huì)出現(xiàn)問題,如果是每個(gè)線程中new SqlSession一個(gè)众雷,而這個(gè)SqlSession只在這一個(gè)線程中使用灸拍,那么肯定是沒問題的嚷那。
/**
*
* @param execType 執(zhí)行器類型
* @param level 事務(wù)隔離級(jí)別
* @param autoCommit 是否自動(dòng)提交
* @return
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 獲取數(shù)據(jù)庫配置環(huán)境
final Environment environment = configuration.getEnvironment();
// 獲取事務(wù)工廠
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 創(chuàng)建事務(wù)
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 生成executor執(zhí)行器
final Executor executor = configuration.newExecutor(tx, execType);
// 生成默認(rèn)SqlSession,這個(gè)是非線程安全击碗,所以每次需要開啟一個(gè)新的。
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
1.1 SqlSession的接口和實(shí)現(xiàn)類
下面看下SqlSession接口定義的方法
這里插下Java泛型的定義戈锻。T 單個(gè)Object编兄,E 集合的element轩性,K和V是指Map的key,value
// @param <T> the returned object type
<T> T selectOne(String statement, Object parameter);
// @param <E> the returned list element type
<E> List<E> selectList(String statement, Object parameter);
// * @param <K> the returned Map keys type
// * @param <V> the returned Map values type
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
DefaultSqlSessioin的構(gòu)造方法:需要configuration狠鸳,executor揣苏,autoCommit
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
2.executor接口
構(gòu)建DefaultSqlSession,需要參數(shù)executor接口的實(shí)現(xiàn)類件舵。默認(rèn)采用SimpleExcutor
不過還有一個(gè)CachingExecutor
不能忽視P恫臁!铅祸!因?yàn)槭悄J(rèn)的坑质。
創(chuàng)建的代碼如下:
/**
* 創(chuàng)建一個(gè)執(zhí)行器,默認(rèn)是SIMPLE
*
* @param transaction
* @param executorType
* @return
*/
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
有個(gè)ExecutorType的枚舉類临梗,不同的執(zhí)行器有不同的用途涡扼。
/**
* 執(zhí)行器類型
*
* SIMPLE:普通的執(zhí)行器
* REUSE:會(huì)重用預(yù)處理語句(prepared statements)
* BATCH:執(zhí)行器將重用語句并執(zhí)行批量更新
*
* @author Clinton Begin
*/
public enum ExecutorType {
SIMPLE, REUSE, BATCH
}
3. DefaultSqlSession實(shí)現(xiàn)類分析
查詢數(shù)據(jù)邏輯,查詢單個(gè)也是使用查詢list結(jié)果。
@Override
public <T> T selectOne(String statement) {
return this.<T>selectOne(statement, null);
}
@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.<T>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) {
return this.selectList(statement, 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();
}
}
重點(diǎn)語句是盟庞,通過statement獲取MappedStatement吃沪,然后包裝參數(shù),執(zhí)行查詢什猖。
這里的statement是指mybatis的namespace+select語句的id巷波,組成的。通過configuration里面預(yù)先讀取的Map集合卸伞,找到具體的MappedStatement.
3.1 Configuration的StrictMap引發(fā)的思考
看到上圖中的mappedStatements大家可能會(huì)奇怪抹镊,命名MappedStatement的id只有連個(gè),為啥放進(jìn)去的確有4個(gè)荤傲。核心原因是 StrictMap類:存儲(chǔ)數(shù)據(jù)分方式垮耳。
目前Configuration有6個(gè)屬性是StrictMap,都會(huì)遇到id帶有.,存儲(chǔ)兩分?jǐn)?shù)據(jù)問題,不同的mapper.xml重復(fù)key终佛,在必須加上namespace才能獲取唯一俊嗽。如果存在重復(fù)的key,那么直接報(bào)錯(cuò)铃彰,說明下面的六個(gè)屬性key唯一I芑怼!牙捉!
mappedStatements
竹揍,caches
,resultMaps
邪铲,parameterMaps
芬位,keyGenerators
,
sqlFragments
。必須全部的mapper里面唯一带到。
上面的是題外話昧碉,繼續(xù)回到selectList查詢
3.2 執(zhí)行查詢的具體操作
雖然配置了CachingExecutor,但是沒有配置二級(jí)緩存最終是會(huì)調(diào)用SimpleExecutor的query方法揽惹。實(shí)際是BaseExecutor的方法被饿。先查詢一級(jí)緩存,可以在select語句中配置
flushCache="true"
,實(shí)現(xiàn)先清除本地緩存搪搏。
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.");
}
// 是否清除一級(jí)緩存
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {//一級(jí)緩存不為空
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;
}
上面的代碼就已經(jīng)得知了整個(gè)查詢數(shù)據(jù)庫的業(yè)務(wù)邏輯了锹漱。
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;
}
4.SimpleExecutor的doQuery方法詳解
@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);
}
}
重點(diǎn)是StatementHandler接口,有點(diǎn)類似executor接口的實(shí)現(xiàn)慕嚷,這次是路由方式哥牍!
這里的創(chuàng)建又回到了Configuration
類,不虧是核心類喝检。
第一步:重點(diǎn)有個(gè)interceptorChain.pluginAll
嗅辣,后續(xù)的分頁插件靠這個(gè)啦。
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
// 路由Statement處理器
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());
}
}
// 父類的構(gòu)造方法
public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
第二步:采用BaseStatementHandler的構(gòu)造方法
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
// BoundSql 占位符? 動(dòng)態(tài)其他參數(shù)
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
// 重點(diǎn)方法挠说,參數(shù)處理器和結(jié)果集處理器澡谭。
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
// Configuration的創(chuàng)建參數(shù)處理器:DefaultParameterHandler
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
// Configuration的創(chuàng)建結(jié)構(gòu)集處理器:DefaultResultSetHandler
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
又有Configuration對(duì)象,這個(gè)Configuration對(duì)象完全是貫穿整個(gè)Mybatis核心業(yè)務(wù)损俭。
執(zhí)行完方法后蛙奖,生成StatementHandler實(shí)現(xiàn)類
第三步:SimpleExecutor的prepareStatement方法
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
// 執(zhí)行查詢
handler.<E>queryCursor(stmt);
// 執(zhí)行數(shù)據(jù)庫語句
@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleCursorResultSets(ps);
}
這里才回顧下jdbc基本操作
mybatis采用的是動(dòng)態(tài)代理:PreparedStatementLogger
引出:ResultSetLogger代理類得到ResultSet結(jié)果集。
//DefaultResultSetHandler處理結(jié)果集
@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;
// 獲取結(jié)果包裝類
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 獲取ResultMap映射
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
// 校驗(yàn)判斷rsw不為空且resultMapCount不小于1
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 處理結(jié)果集
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);
}
處理結(jié)果集
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else { //是有一個(gè)resultMap映射時(shí)
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
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);
// 獲取結(jié)果--核心
Object rowValue = getRowValue(rsw, discriminatedResultMap);
// 存儲(chǔ)結(jié)果
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
// 邏輯分頁
private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
rs.absolute(rowBounds.getOffset());
}
} else {
for (int i = 0; i < rowBounds.getOffset(); i++) {
rs.next();
}
}
}
//處理resultMap杆兵,實(shí)際就是判斷Discriminator鑒別器雁仲,discriminator – 使用結(jié)果值來決定使用哪個(gè) resultMap
public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
Set<String> pastDiscriminators = new HashSet<String>();
Discriminator discriminator = resultMap.getDiscriminator();
while (discriminator != null) {
final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
if (configuration.hasResultMap(discriminatedMapId)) {
resultMap = configuration.getResultMap(discriminatedMapId);
Discriminator lastDiscriminator = discriminator;
discriminator = resultMap.getDiscriminator();
if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {
break;
}
} else {
break;
}
}
return resultMap;
}
自動(dòng)映射類型
AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior()
Configuration默認(rèn)的是AutoMappingBehavior.PARTIAL。
有三種自動(dòng)映射等級(jí):
NONE - 禁用自動(dòng)映射琐脏。僅設(shè)置手動(dòng)映射屬性攒砖。
PARTIAL - 將自動(dòng)映射結(jié)果除了那些有內(nèi)部定義內(nèi)嵌結(jié)果映射的(joins).
FULL - 自動(dòng)映射所有缸兔。
默認(rèn)值是PARTIAL,這是有原因的吹艇。當(dāng)使用FULL時(shí)惰蜜,自動(dòng)映射會(huì)在處理join結(jié)果時(shí)執(zhí)行,并且join取得若干相同行的不同實(shí)體數(shù)據(jù)受神,因此這可能導(dǎo)致非預(yù)期的映射抛猖。
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
if (propertyMapping.getNestedResultMapId() != null) {
// the user added a column attribute to a nested result map, ignore it
column = null;
}
if (propertyMapping.isCompositeResult()
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
|| propertyMapping.getResultSet() != null) {
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// issue #541 make property optional
final String property = propertyMapping.getProperty();
if (property == null) {
continue;
} else if (value == DEFERED) {
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
// 賦值,如果value屬性不一致鼻听,容易出現(xiàn)錯(cuò)誤
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// gcode issue #377, call setter on nulls (value is not 'found')
metaObject.setValue(property, value);
}
}
}
return foundValues;
}
// 核心
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
if (propertyMapping.getNestedQueryId() != null) {
return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
} else if (propertyMapping.getResultSet() != null) {
addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK?
return DEFERED;
} else {
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
return typeHandler.getResult(rs, column);
}
}
BaseTypeHandler-->IntegerTypeHandler獲取具體的值類型
這里用到了Mybatis的org.apache.ibatis.reflection.Reflector
類
最后返回multipleResults得到查詢結(jié)構(gòu)