Mybatis自带是使用SqlSession进行数据的增删该查操作,支持一级缓存,存储作用域为同一个SqlSession,当Session flush或close之后,该Session中的所有Cache就将清空。二级缓存建议采用业务处理。因为mybatis支持的二级缓存是存储作用域为Mapper(Namespace),可自定义存储源,如Ehcache,Redis。
1. SqlSession入口
sqlSessionFactory在上一篇仔细分析了:Mybatis源码第一篇:sqlSessionFactory创建流程. 下面以查询语句分析sqlsession
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("user.selectOne",account);
SqlSession是执行增删改查的接口,默认实现类是:DefaultSqlSession
,主要这个类是非线程安全。非线程安全是指多线程操作同一个对象可能会出现问题,如果是每个线程中new SqlSession一个,而这个SqlSession只在这一个线程中使用,那么肯定是没问题的。
/**
*
* @param execType 执行器类型
* @param level 事务隔离级别
* @param autoCommit 是否自动提交
* @return
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取数据库配置环境
final Environment environment = configuration.getEnvironment();
// 获取事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 创建事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 生成executor执行器
final Executor executor = configuration.newExecutor(tx, execType);
// 生成默认SqlSession,这个是非线程安全,所以每次需要开启一个新的。
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的接口和实现类
下面看下SqlSession接口定义的方法
这里插下Java泛型的定义。T 单个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的构造方法:需要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接口
构建DefaultSqlSession,需要参数executor接口的实现类。默认采用SimpleExcutor
不过还有一个CachingExecutor
不能忽视!!!因为是默认的。
创建的代码如下:
/**
* 创建一个执行器,默认是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;
}
有个ExecutorType的枚举类,不同的执行器有不同的用途。
/**
* 执行器类型
*
* SIMPLE:普通的执行器
* REUSE:会重用预处理语句(prepared statements)
* BATCH:执行器将重用语句并执行批量更新
*
* @author Clinton Begin
*/
public enum ExecutorType {
SIMPLE, REUSE, BATCH
}
3. DefaultSqlSession实现类分析
查询数据逻辑,查询单个也是使用查询list结果。
@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();
}
}
重点语句是,通过statement获取MappedStatement,然后包装参数,执行查询。
这里的statement是指mybatis的namespace+select语句的id,组成的。通过configuration里面预先读取的Map集合,找到具体的MappedStatement.
3.1 Configuration的StrictMap引发的思考
看到上图中的mappedStatements大家可能会奇怪,命名MappedStatement的id只有连个,为啥放进去的确有4个。核心原因是 StrictMap类:存储数据分方式。
目前Configuration有6个属性是StrictMap,都会遇到id带有.,存储两分数据问题,不同的mapper.xml重复key,在必须加上namespace才能获取唯一。如果存在重复的key,那么直接报错,说明下面的六个属性key唯一!!!
mappedStatements
,caches
,resultMaps
,parameterMaps
,keyGenerators
,
sqlFragments
。必须全部的mapper里面唯一。
上面的是题外话,继续回到selectList查询
3.2 执行查询的具体操作
虽然配置了CachingExecutor,但是没有配置二级缓存最终是会调用SimpleExecutor的query方法。实际是BaseExecutor的方法。先查询一级缓存,可以在select语句中配置
flushCache="true"
,实现先清除本地缓存。
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++;
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 {
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;
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);
}
}
重点是StatementHandler接口,有点类似executor接口的实现,这次是路由方式!
这里的创建又回到了Configuration
类,不亏是核心类。
第一步:重点有个interceptorChain.pluginAll
,后续的分页插件靠这个啦。
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());
}
}
// 父类的构造方法
public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
第二步:采用BaseStatementHandler的构造方法
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 占位符? 动态其他参数
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
// 重点方法,参数处理器和结果集处理器。
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
// Configuration的创建参数处理器: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的创建结构集处理器: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对象,这个Configuration对象完全是贯穿整个Mybatis核心业务。
执行完方法后,生成StatementHandler实现类
第三步: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;
}
// 执行查询
handler.<E>queryCursor(stmt);
// 执行数据库语句
@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleCursorResultSets(ps);
}
这里才回顾下jdbc基本操作
mybatis采用的是动态代理:PreparedStatementLogger
引出:ResultSetLogger代理类得到ResultSet结果集。
//DefaultResultSetHandler处理结果集
@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);
// 获取ResultMap映射
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
// 校验判断rsw不为空且resultMapCount不小于1
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);
}
处理结果集
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 { //是有一个resultMap映射时
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);
// 获取结果--核心
Object rowValue = getRowValue(rsw, discriminatedResultMap);
// 存储结果
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,实际就是判断Discriminator鉴别器,discriminator – 使用结果值来决定使用哪个 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;
}
自动映射类型
AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior()
Configuration默认的是AutoMappingBehavior.PARTIAL。
有三种自动映射等级:
NONE - 禁用自动映射。仅设置手动映射属性。
PARTIAL - 将自动映射结果除了那些有内部定义内嵌结果映射的(joins).
FULL - 自动映射所有。
默认值是PARTIAL,这是有原因的。当使用FULL时,自动映射会在处理join结果时执行,并且join取得若干相同行的不同实体数据,因此这可能导致非预期的映射。
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属性不一致,容易出现错误
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得到查询结构