XMLLanguageDriver
创建createParameterHandler
代码如下:
@Ov@Override public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql); }
InterceptorChain.pluginAll
InterceptorChain
内部维护了一个List<Interceptor> interceptors
,pluginAll方法就是对目标对象层层代理。
publ public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
我们可以看下mybatis
中的Interceptor
在这里插入代码片
package org.apache.ibatis.plugin; import java.util.Properties; public interface Interceptor { Object intercept(Invocation invocation) throws Throwable; Object plugin(Object target); void setProperties(Properties properties); }
其提供了了plugin方法用来生成代理对象,intercept方法用来在执行目标方法前进行拦截处理。
如下所示,是mybatisplus中分页插件实现的plugin方法。其调用了Plugin.wrap(target, this)来为StatementHandler实例对象生成代理对象。
@Override public Object plugin(Object target) { if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } return target; }
mybatis
中的org.apache.ibatis.plugin.Plugin
主要属性和方法如下所示:
public class Plugin implements InvocationHandler { private final Object target; private final Interceptor interceptor; private final Map<Class<?>, Set<Method>> signatureMap; private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { this.target = target; this.interceptor = interceptor; this.signatureMap = signatureMap; } public static Object wrap(Object target, Interceptor interceptor) { Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); Class<?> type = target.getClass(); Class<?>[] interfaces = getAllInterfaces(type, signatureMap); if (interfaces.length > 0) { return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } return target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Set<Method> methods = signatureMap.get(method.getDeclaringClass()); if (methods != null && methods.contains(method)) { return interceptor.intercept(new Invocation(target, method, args)); } return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } } //... }
可以看到Plugin
实现了InvocationHandler
接口。其wrap
方法为target
生成了代理对象,当执行目标方法时,会首先触发Plugin
的invoke
方法。在invoke
方法里面调用了interceptor
的intercept
方法。
newResultSetHandler
pub 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; }
代码解释如下:
- ① 创建DefaultResultSetHandler实例;
- ②
interceptorChain.pluginAll
对resultSetHandler 进行层层代理包装,返回代理对象。
顺便看一下我们最后得到的StatementHandler
③ SimpleExecutor.prepareStatement
SimpleExecutor.prepareStatement(StatementHandler, Log) 方法源码如下,这里handler就是前面创建的RoutingStatementHandler .
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; }
代码解释如下:
① stmt = handler.prepare(connection, transaction.getTimeout());意识是使用StatementHandler 进行预处理获取Statement 对象。
② handler.parameterize(stmt)使用handler对①获取的Statement 对象进行参数解析。
RoutingStatementHandler交给了其装饰的对象PreparedStatementHandle处理,PreparedStatementHandle调用了其抽象父类BaseStatementHandler处理。
@Override public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { return delegate.prepare(connection, transactionTimeout); }
BaseStatementHandler
的prepare
方法
@Override public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { statement = instantiateStatement(connection); setStatementTimeout(statement, transactionTimeout); 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对象;
- ② 设置超时时间;
- ③ 设置每次批量返回的结果行数
PreparedStatementHandler
的instantiateStatement
方法
@Override 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); } }
代码解释如下:
① 获取处理前的SQL,这时候带有占位符?
select id,last_name lastName,email,gender from tbl_employee where id = ?
② 如果KeyGenerator是Jdbc3KeyGenerator类型,则根据keyColumnNames 是否为null进行不同处理;
③ 如果ResultSetType不为null,则根据ResultSetType获取Statement对象;
④ 否则只根据SQL获取Statement对象。
如下图所示,这里获取的Statement对象也是一个代理对象,实际对象是com.mysql.jdbc.JDBC42PreparedStatement@4dbb42b7: select id,last_name lastName,email,gender from tbl_employee where id = ** NOT SPECIFIED **。
handler.parameterize(stmt)
如下所示,这里RoutingStatementHandler交给了其装饰对象PreparedStatementHandler去处理。
RoutingStatementHandler的parameterize方法如下:
@Override public void parameterize(Statement statement) throws SQLException { delegate.parameterize(statement); }
PreparedStatementHandler的parameterize方法如下
@O@Override public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); }
这里parameterHandler如下所示:
DefaultParameterHandler的setParameters方法
@Override public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params 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); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { 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); } } } } }
代码解释如下:
① 根据boundSql获取parameterMappings进行遍历处理;
② 如果parameterMapping.getMode() == ParameterMode.OUT,直接跳过进行下次循环;
③ 获取propertyName和value;
④ typeHandler.setParameter(ps, i + 1, value, jdbcType)对本地循环的参数进行赋值
我们对第④步详细跟踪一下。默认情况下,如果不设置TypeHandler,那么这里获取到的TypeHandler实例为UnknownTypeHandler,其继承于BaseTypeHandler。这里会走到UnknownTypeHandler的setNonNullParameter方法。
@Override public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException { TypeHandler handler = resolveTypeHandler(parameter, jdbcType); handler.setParameter(ps, i, parameter, jdbcType); }
可以看到,其首先根据参数与jdbcType解析handler ,然后使用handler
为ps
中参数占位符赋值。
其解析TypeHandler主要逻辑如下所示:
private TypeHandler<? extends Object> resolveTypeHandler(Object parameter, JdbcType jdbcType) { TypeHandler<? extends Object> handler; if (parameter == null) { handler = OBJECT_TYPE_HANDLER; } else { handler = typeHandlerRegistry.getTypeHandler(parameter.getClass(), jdbcType); // check if handler is null (issue #270) if (handler == null || handler instanceof UnknownTypeHandler) { handler = OBJECT_TYPE_HANDLER; } } return handler; }
代码解释如下:
- ① 如果
parameter
为null
,则handler
为ObjectTypeHandler
; - ② 否则根据参数类型、
jdbcType
从typeHandlerRegistry
的TYPE_HANDLER_MAP
集合中获取;
处理完后的statement
实例如下所示,SQL
中参数占位符已经被赋予实际值,$Proxy6
表示其是一个代理对象。
④ handler.<E>query(stmt, resultHandler)
RoutingStatementHandler
交给装饰对象PreparedStatementHandler
进行查询。
@Ov@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { return delegate.<E>query(statement, resultHandler); }
PreparedStatementHandler
的query
方法如下。
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps); }
代码解释如下:
- ① 转换为
PreparedStatement
类型然后调用其execute
方法,由于其是代理对象,故而会先触发其调用处理程序PreparedStatementLogger
的invoke
方法。 - ② 使用
DefaultResultSetHandler
处理结果集
PreparedStatementLogger.invoke方法
EXECUTE_METHODS
值如下:
[addBatch, execute, executeUpdate, executeQuery]
SET_METHODS
值如下:
[setBytes, setFloat, setBigDecimal, setByte, setNull, setShort, setObject, setInt, setArray, setNCharacterStream, setDouble, setNClob, setString, setLong, setCharacterStream, setAsciiStream, setClob, setBlob, setDate, setBinaryStream, setNString, setTimestamp, setTime, setBoolean]
@Override public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, params); } //[addBatch, execute, executeUpdate, executeQuery] if (EXECUTE_METHODS.contains(method.getName())) { if (isDebugEnabled()) { debug("Parameters: " + getParameterValueString(), true); } clearColumnInfo(); if ("executeQuery".equals(method.getName())) { ResultSet rs = (ResultSet) method.invoke(statement, params); return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack); } else { return method.invoke(statement, params); } } else if (SET_METHODS.contains(method.getName())) { if ("setNull".equals(method.getName())) { setColumn(params[0], null); } else { setColumn(params[0], params[1]); } return method.invoke(statement, params); } else if ("getResultSet".equals(method.getName())) { ResultSet rs = (ResultSet) method.invoke(statement, params); return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack); } else if ("getUpdateCount".equals(method.getName())) { int updateCount = (Integer) method.invoke(statement, params); if (updateCount != -1) { debug(" Updates: " + updateCount, false); } return updateCount; } else { return method.invoke(statement, params); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }
代码解释如下:
① 判断方法所属声明类型为object,则直接反射调用方法;
② 判断方法名是否在EXECUTE_METHODS内,进而判断是否为executeQuery,进行不同处理;
③ 判断方法名是否在SET_METHODS内,如果是,则首先进行setColumn处理;
④ 判断方法名是否为getResultSet;
⑤ 判断方法名是否为getUpdateCount;
⑥ 以上都没有执行,则直接进行方法反射。如下图所示,方法为public abstract boolean java.sql.PreparedStatement.execute() throws java.sql.SQLException
。
statement对象为JDBC42PreparedStatement,那么方法最终会走到PreparedStatement.execute() 方法。在MySQL5中public class PreparedStatement extends com.mysql.jdbc.StatementImpl implements java.sql.PreparedStatement这里可以看一下其execute
方法(这里不用做深入研究,只需要知道方法执行后,就拿到了结果集resultSet
):
public boolean execute() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { MySQLConnection locallyScopedConn = this.connection; if (!checkReadOnlySafeStatement()) { throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") + Messages.getString("PreparedStatement.21"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } ResultSetInternalMethods rs = null; CachedResultSetMetaData cachedMetadata = null; this.lastQueryIsOnDupKeyUpdate = false; if (this.retrieveGeneratedKeys) { this.lastQueryIsOnDupKeyUpdate = containsOnDuplicateKeyUpdateInSQL(); } clearWarnings(); setupStreamingTimeout(locallyScopedConn); this.batchedGeneratedKeys = null; Buffer sendPacket = fillSendPacket(); String oldCatalog = null; if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) { oldCatalog = locallyScopedConn.getCatalog(); locallyScopedConn.setCatalog(this.currentCatalog); } // // Check if we have cached metadata for this query... // if (locallyScopedConn.getCacheResultSetMetadata()) { cachedMetadata = locallyScopedConn.getCachedMetaData(this.originalSql); } Field[] metadataFromCache = null; if (cachedMetadata != null) { metadataFromCache = cachedMetadata.fields; } boolean oldInfoMsgState = false; if (this.retrieveGeneratedKeys) { oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled(); locallyScopedConn.setReadInfoMsgEnabled(true); } // // Only apply max_rows to selects // locallyScopedConn.setSessionMaxRows(this.firstCharOfStmt == 'S' ? this.maxRows : -1); rs = executeInternal(this.maxRows, sendPacket, createStreamingResultSet(), (this.firstCharOfStmt == 'S'), metadataFromCache, false); if (cachedMetadata != null) { locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, cachedMetadata, rs); } else { if (rs.reallyResult() && locallyScopedConn.getCacheResultSetMetadata()) { locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, null /* will be created */, rs); } } if (this.retrieveGeneratedKeys) { locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState); rs.setFirstCharOfQuery(this.firstCharOfStmt); } if (oldCatalog != null) { locallyScopedConn.setCatalog(oldCatalog); } if (rs != null) { this.lastInsertId = rs.getUpdateID(); this.results = rs; } return ((rs != null) && rs.reallyResult()); } }
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; // 得到第一个ResultSet,也就是before the first row ResultSetWrapper rsw = getFirstResultSet(stmt); // 获取当前statement的实体-属性结果映射 List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); 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); }
代码解释如下:
① 获取ResultSetWrapper ;
② 获取resultMaps ;
③ 对resultMaps进行循环处理;
④ 获取resultSets 然后进行循环处理;
⑥ 如果multipleResults集合只有一个元素则返回第一个元素,否则返回multipleResults获取到的ResultSetWrapper rsw
如下图所示:
此时指针并没有指向数据集的第一条,而是Before start of result set
,currRowIndex=-1
。rs是个代理对象。
获取到的 List<ResultMap> resultMaps
如下图所示(xml中当前查询没有指定resultMap,这是mybatis默认创建的
):
这里我们跟踪一下handleResultSet(rsw, resultMap, multipleResults, null);
如下所示。
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()); } }
这里会创建默认的DefaultResultHandler 实例对象,然后调用核心方法handleRowValues处理行记录值。这里会把defaultResultHandler.getResultList()放到multipleResults中,multipleResults是一个List<Object>。DefaultResultHandler 只有一个List<Object> list
属性,其作为当前上下文处理结果的容器,存放结果集(RowValue或者说ResultObject集合)。
public class DefaultResultHandler implements ResultHandler<Object> { private final List<Object> list; public DefaultResultHandler() { list = new ArrayList<Object>(); } @SuppressWarnings("unchecked") public DefaultResultHandler(ObjectFactory objectFactory) { list = objectFactory.create(List.class); } @Override public void handleResult(ResultContext<? extends Object> context) { list.add(context.getResultObject()); } public List<Object> getResultList() { return list; } }
DefaultResultSetHandler.handleRowValues
方法如下:
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 { handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } }
会根据是否有嵌套结果映射来走不同的处理逻辑,这里我们走的是handleRowValuesForSimpleResultMap
。
DefaultResultSetHandler.handleRowValuesForSimpleResultMap
方法如下所示:
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()); } }
代码解释如下:
① 获取resultContext 然后跳到下一行(第一次next方法执行前指针位置是before start of resultset);
② 如果有下一行记录需要处理并且有对应的resultSet,则进行迭代循环处理;
③ 处理Discriminator;
④ 获取RowValue
也就是行记录返回值,这里是Employee
对象。
- ⑤
storeObject
方法会把当前context.getResultObject()
放到DefaultResultHandler
的成员List<Object> list
中
获取行记录返回结果方法如下所示:
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException { final ResultLoaderMap lazyLoader = new ResultLoaderMap(); //这里是空对象 空对象,在下面过程中会对其进行属性赋值 Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null); if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { final MetaObject metaObject = configuration.newMetaObject(resultObject); boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty(); if (shouldApplyAutomaticMappings(resultMap, false)) { foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues; } foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues; foundValues = lazyLoader.size() > 0 || foundValues; resultObject = foundValues ? resultObject : null; return resultObject; } return resultObject; }
代码解释如下:
① 获取返回结果对象resultObject ,这里得到的Employee是一个空对象,成员值都是null;
② 根据resultObject 获取MetaObject 对象;
③ 判断是否可以进行自动映射(也就是查询返回的column与对象property一致),如果是则进行applyAutomaticMappings;
④ 尝试进行applyPropertyMappings,也就是自定义了resultMap,做了列与属性映射;
⑤ 判断是返回null还是resultObject;
处理完当前ResultSet后,会获取nextResultSet,将得到的结果存储起来。迭代处理完后,依次返回。其中这里应用了大量的代理模式、装饰模式以及mybatis结果映射的知识。另外,这里只是在单mybatis环境下对单个查询进行分析,那么在SSM、SpringBoot环境下的流程细节可能有所不同。这里补充一下ResultSet、ResultSetImpl与NativeResultset获取数据的过程。
ResultSet是一个接口,我们代码中看到的也是一个代理对象(可能是多次代理哦,比如ResultSetImpl-HikariProxyResultSet-org.apache.ibatis.logging.jdbc.ResultSetLogger),其会将操作委派给ResultSetImpl去处理。
ResultSetImpl继承自NativeResultset,后者拥有属性ResultsetRows rowData维护了数据库查询的数据以及属性Row thisRow维护当前数据。每一次的resultSet.next()造成的结果就是this.thisRow = this.rowData.next();。
rowData如下图所示,内维护了一个内部字节数组,存放每一行的非空属性。数组下标对应列定义下标。
那么列定义是什么?直接看图吧
rowData什么时候被赋值的?
在执行完数据库查询(或其他操作)后创建ResultSetImpl时为rowData赋值,多行结果集时rowData中rows有多个元素,如下图所示:
参考博文: