- 在研究select執(zhí)行過程之前先來介紹一個重要的類
一、SqlNode和SqlSource
- 在myabtis初始化過程中可以知道,映射配置文件中的sql節(jié)點會被解析為MappedStatement對象,其中sql語句解析成SqlSource對象,sql語句中定義的Sql節(jié)點、文本節(jié)點等,則由SqlNode接口的響應實現(xiàn)。
1、SqlSource接口

SqlSource接口結構
public interface SqlSource {
//通過解析得到BoundSql對象,其中封裝了包含?占位符號的sql語句以及綁定的實參
BoundSql getBoundSql(Object parameterObject);
}
2、SqlNode
public interface SqlNode {
//根據(jù)用戶傳入的實際參數(shù),解析該sqlNode所記錄的動態(tài)的sql節(jié)點,當sql節(jié)點下的所有sqlnode
//完成解析后,我們就可以從dynamicContext中獲取一條動態(tài)生成的完整的sql語句。
boolean apply(DynamicContext context);
}
- sqlNode有很多的實現(xiàn)類,比如ChooseSqlNode、ForEachSqlNode等
二、StatementHandler
-
核心接口,完成mybatis中最核心的工作,接口功能很多,例如創(chuàng)建statement對象,為sql語句綁定實際參數(shù),執(zhí)行select、insert等多種類型的sql語句,并將結果集映射成結果對象。
StatementHandler類結構圖
1、RoutingStatementHandler
- 很根據(jù)MapperdStatement中指定的statementType字段,創(chuàng)建相應的StatementHandler接口實現(xiàn)。
2、BaseStatementHandler
- 實現(xiàn)了Statementhandler接口的抽象類,只提供了參數(shù)綁定的相關方法,并沒有具體操作數(shù)據(jù)庫的方法。依賴于ParameterHandler和ResultSetHandler組件
- 實現(xiàn)了 StatementHandler接口中的prepare方法,該方法中調用instantiateStatement抽象方法初始化statement對象,然后配置相關的參數(shù)
3、PreparedStatementHandler等
- 是BaseStatementHandler的實現(xiàn)類,是具體操作sql的地方
三、ResultHandler
- StatementHandler接口在執(zhí)行完指定的select語句之后,會將查詢得到的結果集交給ResultSetHandler完成映射處理,ResultSetHandler除了負責映射select語句得到的結果集還會處理存儲過程執(zhí)行后的輸出參數(shù)
public interface ResultSetHandler {
//處理結果集生成相應的結果對象集合
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
//處理結果集,返回相應的游標對象
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
//處理存儲過程的輸出對象
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
1、DefaultResultSetHandler實現(xiàn)類
- 重要的屬性:
private final Executor executor;
private final Configuration configuration;
private final MappedStatement mappedStatement;
private final RowBounds rowBounds;
private final ParameterHandler parameterHandler;
//用戶指定用于處理結果的ResultHandler對象
private final ResultHandler<?> resultHandler;
private final BoundSql boundSql;
private final TypeHandlerRegistry typeHandlerRegistry;
private final ObjectFactory objectFactory;
private final ReflectorFactory reflectorFactory;
四、select的執(zhí)行流程
1、方法入口,執(zhí)行代理接口的invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判斷方法的父類是否是Object如果是就直接調用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
//獲取MapperMethod并緩存
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private final Map<Method, MapperMethod> methodCache;
1、獲取MapperMethod時候先判斷methodCache有沒有,沒有的話那么直接創(chuàng)建然后放入methodCache中(下次調用直接從map中獲取)
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
//構建mapperMethod方法
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
2、創(chuàng)建MapperMethod對象,SqlCommand和MethodSignature是MapperMethod的內部類
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
//SqlCommand包含了mapper.xml文件中的相關sql語句及屬性信息
this.command = new SqlCommand(config, mapperInterface, method);
//MethodSignature包含了mapper接口方法簽名的信息
this.method = new MethodSignature(config, method);
}
2.1、獲取SqlCommand
public SqlCommand(Configuration configuration, Class<?> declaringInterface, Method method) throws BindingException {
//包名+方法名作為唯一的id
name = declaringInterface.getName() + "." + method.getName();
final MappedStatement ms;
try {
//根據(jù)id獲取
ms = configuration.getMappedStatement(name);
} catch (Exception e) {
throw new BindingException("Invalid bound statement (not found): " + name, e);
}
//獲取crud類型如果是unknown就報錯誤
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
2.2、獲取MethodSignature
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
2、執(zhí)行MapperMethod的execute方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//根據(jù)SqlCommand類型判斷是執(zhí)行curd的哪一個
//insert
if (SqlCommandType.INSERT == command.getType()) {
//將參數(shù)轉換為sql命令的參數(shù)
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
//update
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
//delete
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
//select
} else if (SqlCommandType.SELECT == command.getType()) {
//MethodSignature方法簽名的返回類型來進行不同的邏輯處理
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else {
//當不滿足上面需求的時候進行這一步操作,先進行參數(shù)解析,包裝為map返回
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
3、DefaultSqlSession中執(zhí)行sqlSession.selectOne方法
public <T> T selectOne(String statement, Object parameter) {
// 調用selectList獲取list列表,然后獲取第一個。如果有多個那么就直接報錯誤
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;
}
}
4、歸根結底還是調用executor的query方法來執(zhí)行
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
////根據(jù)statementId獲取MappedStatement,MappedStatement封裝了增刪改查的詳細信息
MappedStatement ms = configuration.getMappedStatement(statement);
//調用executor的query
List<E> result = executor.<E>query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
return result;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
//包裝參數(shù)為map類型
private Object wrapCollection(final Object object) {
if (object instanceof List) {
StrictMap<Object> map = new StrictMap<Object>();
map.put("list", object);
return map;
} else if (object != null && object.getClass().isArray()) {
StrictMap<Object> map = new StrictMap<Object>();
map.put("array", object);
return map;
}
return object;
}
5、執(zhí)行cachingExecutor的query方法,
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//獲取解析過動態(tài)標簽的sql,BoundSql包含當前sql語句的詳細信息和參數(shù)
BoundSql boundSql = ms.getBoundSql(parameterObject);
//創(chuàng)建緩存key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
6、獲取BoundSql
public BoundSql getBoundSql(Object parameterObject) {
/*獲取解析過動態(tài)標簽的sql*/
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.size() <= 0) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
// 檢查參數(shù)映射中是否存在嵌套的resultMap并設置boolean標記
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;
}
7、執(zhí)行BaseExecutor的query
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//獲取本地緩存中的數(shù)據(jù)
Cache cache = ms.getCache();
//緩存為空則直接調用
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, key, parameterObject, boundSql);
if (!dirty) {
cache.getReadWriteLock().readLock().lock();
try {
@SuppressWarnings("unchecked")
List<E> cachedList = (List<E>) cache.getObject(key);
if (cachedList != null) return cachedList;
} finally {
cache.getReadWriteLock().readLock().unlock();
}
}
List<E> list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
return list;
}
}
return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
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();
}
deferredLoads.clear(); // issue #601
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache(); // issue #482
}
}
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 {
//子類實現(xiàn)具體的查詢邏輯
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;
}
8、這里在SimpleExecutor中真正的執(zhí)行query(根據(jù)配置還可以在選擇其他的Executor)
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();
//創(chuàng)建statementhandler,statementhandler是創(chuàng)建原生statement的處理器
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
//準備Statement
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
9、構建StatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
/*構造RoutingStatementHandler*/
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//攔截器執(zhí)行
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
//終于柳暗花明了
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//根據(jù)MappedStatement中的statement類型來創(chuàng)建響應的statement處理器
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());
}
}
10、statement的準備
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//獲取數(shù)據(jù)庫連接
Connection connection = getConnection(statementLog);
//進行響應的statement的轉唄工作
stmt = handler.prepare(connection);
//給statement設置參數(shù)
handler.parameterize(stmt);
return stmt;
}
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//實例化對象的statement
statement = instantiateStatement(connection);
//設置超時時間
setStatementTimeout(statement);
//設置FetchSize
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
實例化過程以預處理statement為例:
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareStatement(sql);
}
}
11、設置參數(shù)過程
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
//遍歷參數(shù)綁定映射列表
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
//獲取參數(shù)綁定的屬性名稱
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);
//根據(jù)屬性名稱從參數(shù)對象中反射獲取對應的值
value = metaObject.getValue(propertyName);
}
//獲取類型處理器
TypeHandler typeHandler = parameterMapping.getTypeHandler();
//獲取jdbcType類型
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
/*為PreparedStatement設置值*/
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
12、最后執(zhí)行查詢并進行參數(shù)封裝
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//執(zhí)行sql語句
ps.execute();
//參數(shù)封裝
return resultSetHandler.<E> handleResultSets(ps);
}

select執(zhí)行流程
