Mybatis源码系列文章
手写源码(了解源码整体流程及重要组件)
Mybatis源码解析(一):环境搭建
Mybatis源码解析(二):全局配置文件的解析
Mybatis源码解析(三):映射配置文件的解析
Mybatis源码解析(四):sql语句及#{}、${}的解析
Mybatis源码解析(五):SqlSession会话的创建
Mybatis源码解析(六):缓存执行器操作流程
selectOne方法
@Override
public T selectOne(String statement, Object parameter) {// Popular vote was to return null on 0 results and throw exception on too many.List 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;}
}
selectList方法
// 第一个
@Override
public List selectList(String statement, Object parameter) {// 调用重载方法return this.selectList(statement, parameter, RowBounds.DEFAULT);
}// 第二个
@Override
public List selectList(String statement, Object parameter, RowBounds rowBounds) {// 参数4:结果集处理器return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
}// 第三个
private List selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {try {// 根据传入的statementId,获取MappedStatement对象MappedStatement ms = configuration.getMappedStatement(statement);// 调用执行器的查询方法// wrapCollection(parameter)是用来装饰集合或者数组参数return executor.query(ms, wrapCollection(parameter), rowBounds, handler);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}
1. 从MappedStatement对象中获取BoundSql对象
2. 获取一级或二级缓存Map的key,value是查询的结果
@Override
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {// 获取绑定的SQL语句,比如 "SELECT * FROM user WHERE id = ? "BoundSql boundSql = ms.getBoundSql(parameterObject);// 生成缓存KeyCacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public BoundSql getBoundSql(Object parameterObject) {BoundSql boundSql = sqlSource.getBoundSql(parameterObject);List parameterMappings = boundSql.getParameterMappings();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;
}
进入getBoundSql
@Override
public BoundSql getBoundSql(Object parameterObject) {return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}
进入CachingExecutor的createCacheKey方法
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
}
进入BaseExecutor的createCacheKey方法
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {if (closed) {throw new ExecutorException("Executor was closed.");}// 创建cacheKey对象CacheKey cacheKey = new CacheKey();// idcacheKey.update(ms.getId());// 分页参数cacheKey.update(rowBounds.getOffset());cacheKey.update(rowBounds.getLimit());// sqlcacheKey.update(boundSql.getSql());List parameterMappings = boundSql.getParameterMappings();TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();// mimic DefaultParameterHandler logicfor (ParameterMapping parameterMapping : parameterMappings) {if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) {value = boundSql.getAdditionalParameter(propertyName);} else if (parameterObject == null) {value = null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {value = parameterObject;} else {MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}// 参数的值cacheKey.update(value);}}if (configuration.getEnvironment() != null) {// issue #176// 当前环境的值也会设置cacheKey.update(configuration.getEnvironment().getId());}return cacheKey;
}
...
private static final int DEFAULT_MULTIPLIER = 37;
private static final int DEFAULT_HASHCODE = 17;// 参与hash运算的乘数
private final int multiplier;
// cachekey的hash值,在update函数中实时算出来
private int hashcode;
// 校验和,hash值的和
private long checksum;
// updateList的中的元素个数
private int count;
// 根据该集合中的元素判断两个cacheKey是否相同
private List
public void update(Object object) {// 获取参数的object的hash值int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);// 更新count 、checksum以及hashcode的值count++;checksum += baseHashCode;baseHashCode *= count;hashcode = multiplier * hashcode + baseHashCode;// 将对象添加到list集合中updateList.add(object);
}
equals和hashCode方法
@Override
public boolean equals(Object object) {if (this == object) { // 比较是不是同一个对象return true;}if (!(object instanceof CacheKey)) { // 是否类型相同return false;}final CacheKey cacheKey = (CacheKey) object;if (hashcode != cacheKey.hashcode) { // hashcode是否相同return false;}if (checksum != cacheKey.checksum) { // checksum是否相同return false;}if (count != cacheKey.count) { // count是否相同return false;}// 按顺序比较updateList中元素的hash值是否相同for (int i = 0; i < updateList.size(); i++) {Object thisObject = updateList.get(i);Object thatObject = cacheKey.updateList.get(i);if (!ArrayUtil.equals(thisObject, thatObject)) {return false;}}return true;
}@Override
public int hashCode() {return hashcode;
}
@Override
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {// 获取二级缓存Cache cache = ms.getCache();if (cache != null) {// 刷新二级缓存 (存在缓存且flushCache为true时)flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, boundSql);@SuppressWarnings("unchecked")// 从二级缓存中查询数据List list = (List) tcm.getObject(cache, key);// 如果二级缓存中没有查询到数据,则查询一级缓存及数据库if (list == null) {// 委托给BaseExecutor执行list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);// 将查询结果 要存到二级缓存中(注意:此处只是存到map集合中,没有真正存到二级缓存中)tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}// 委托给BaseExecutor执行return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public List 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.");}// 1. 如果配置了flushCacheRequired为true,则会在执行器执行之前就清空本地一级缓存if (queryStack == 0 && ms.isFlushCacheRequired()) {// 1.1. 清空缓存clearLocalCache();}List list;try {// 2. 查询堆栈 + 1queryStack++;// 从一级缓存中获取数据list = resultHandler == null ? (List) localCache.getObject(key) : null;if (list != null) {// 3.1. 已有缓存结果,则处理本地缓存结果输出参数(存储过程)handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {// 3.2. 没有缓存结果,则从数据库查询结果list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {// 查询堆栈数 -1queryStack--;}if (queryStack == 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;
}
进入queryFromDatabase方法
private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List list;// 1. 首先向本地缓存中存入一个ExecutionPlaceholder的枚举类占位valuelocalCache.putObject(key, EXECUTION_PLACEHOLDER);try {// 2. 执行doQuery方法list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {// 3. 执行完成移除这个keylocalCache.removeObject(key);}// 4. 查询结果存入缓存中localCache.putObject(key, list);// 5. 如果MappedStatement的类型为CALLABLE,则向localOutputParameterCache缓存中存入value为parameter的缓存if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;
}