前面的博客已经介绍了mybatis
的主配置文件的解析以及mapper
文件的解析的全流程,我们已经得知mapper
文件解析有两种情况,一种是只有#
占位符的SQL
语句,这种查询语句会将#
转成?
。还有一种就是含有$
或者动态标签的SQL
语句,这个时候不做任何处理。今天针对这两种情况,我们来分析一下源码,不过只分析select
查询,至于还有insert delete update
会在下篇博客中详细说明。下面来书写我们的测试代码,具体的代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.DemoMapper">
<select id="selectById" parameterType="int" resultType="map">
select * from mybooks where id =#{id};
</select>
<select id="selectAll" parameterType="String" resultType="Map">
select * from mybooks
<where>
1=1
<if test="param1 != null and param1 != ''">
and author = ${param1}
</if>
and title = #{param2}
</where>
</select>
</mapper>
public interface DemoMapper {
List<Map<String, Object>> selectAll(String author, String title);
List<Map<String, Object>> selectById(int id);
default void test() {
System.out.println(111);
}
}
public class TestPerform {
public static void main(String[] args) throws IOException {
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);
System.out.println(mapper.selectById(1));
System.out.println(mapper.selectAll("Bob", "Ruby"));
sqlSession.close();
}
}
我们先来看代理类的创建过程。让我们跟随的代码执行的流程,具体代码如下:
public class DefaultSqlSession implements SqlSession {
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
}
public class Configuration {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
}
public class MapperRegistry {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
上面经过一系列调用然后从knownMappers
中取出传入进行的接口类,获取到接口类的代理工厂类。然后会执行mapperProxyFactory.newInstance(sqlSession);
,具体的代码如下:
public class MapperProxyFactory<T> {
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
这个时候会创建MapperProxy
类,将sqlSession
,mapperInterface
,methodCache
参数传入进去。点进去发现就是进行了一些赋值。这个时候重点就是newInstance(mapperProxy)
方法,开始进行代理,用的jdk
的动态代理。具体的代码如下:
public class MapperProxyFactory<T> {
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface },
mapperProxy);
}
}
到此整个代理就创建完成了。这个时候我们mapper
中的查询,就是调用代理类中的查询,让我们先来看看第一种只有#
的查询的SQL
语句的执行流程,具体的代码如下:
public class MapperProxy<T> implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
//你是不是调用的Object默认的方法
return method.invoke(this, args);
} else if (method.isDefault()) {
//对于默认方法的处理
if (privateLookupInMethod == null) {
return invokeDefaultMethodJava8(proxy, method, args);
} else {
return invokeDefaultMethodJava9(proxy, method, args);
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
}
走来会判断你调用的是不是Object
中的方法,如果是,就直接执行。还有就是判断是不是接口中的默认方法,如果是,就直接执行。很显然,这儿不是的,我们继续往下看。会执行cachedMapperMethod(method);
方法,这个方法是查看缓存中有没有数据,有的话,直接从缓存中去。没有的话,就创建一个新的MapperMethod
,这个MapperMethod
对象创建过程我们要看下大概的流程,因为后面在执行的时候有用到,具体代码如下:
public class MapperMethod {
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
}
public class MapperMethod {
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
//selectById
final String methodName = method.getName();
//com.DemoMapper
final Class<?> declaringClass = method.getDeclaringClass();
//根据methodName和declaringClass取出对应的MappedStatement对象
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
//name=com.DemoMapper.selectById
name = ms.getId();
//Select
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
}
public class MapperMethod {
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();
}
//如果返回的是空,returnsVoid就为true
this.returnsVoid = void.class.equals(this.returnType);
//如果返回的是集合或者数据,returnsMany就为true
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) ||
this.returnType.isArray();
//返回的是否是游标对象
this.returnsCursor = Cursor.class.equals(this.returnType);
//返回的是否是Optional对象
this.returnsOptional = Optional.class.equals(this.returnType);
this.mapKey = getMapKey(method);
//返回的是否是map
this.returnsMap = this.mapKey != null;
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
}
上面的代码主要讲了下MapperMethod
对象的创建,对一些重要的参数进行创建,我们继续跟进mapperMethod.execute(sqlSession, args);
方法,具体的代码如下:
public class MapperMethod {
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
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 if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
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;
}
}
这个时候走来会判断是什么操作,很明显这儿是select
查询的操作。由于我们返回的参数是List
,所以这儿的返回类型是returnsMany
,所以会执行executeForMany(sqlSession, args);
的方法,具体的代码如下:
public class MapperMethod {
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
//参数的获取和转换,这儿获取的是1
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
}
这儿走来会调用method.convertArgsToSqlCommandParam(args);
方法,这个是获取参数,由于这儿我们只有一个参数,我们就不细说,在后面第二种查询的时候,我会细说这个参数的获取和转换。然后判断有没有分页的参数,由于mybatis
自带的分页查询不太好用,我们不会说,后面可能会讲分页插件。所以这儿会直接执行sqlSession.selectList(command.getName(), param);
方法,具体的代码如下:
public class DefaultSqlSession implements SqlSession {
@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();
}
}
}
上面的代码获取对应的MappedStatement
对象,然后会执行executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
方法,在执行这个方法之前,先执行了wrapCollection(parameter)
方法,对参数进行了一些包装,我们跟进去看看具体的代码。
public class DefaultSqlSession implements SqlSession {
private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
StrictMap<Object> map = new StrictMap<>();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
}
return map;
} else if (object != null && object.getClass().isArray()) {
StrictMap<Object> map = new StrictMap<>();
map.put("array", object);
return map;
}
return object;
}
}
上面的代码判断是不是集合和数组,如果是集合或者是数组,就会将这值放到StrictMap
对象中去。这是一个Map
对象。这个时候看看executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
具体代码,具体的代码如下:
public class CachingExecutor implements Executor {
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
//确定缓存的key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
}
这个时候走来会执行ms.getBoundSql(parameterObject);
方法,具体的代码如下:
public final class MappedStatement {
public BoundSql getBoundSql(Object parameterObject) {
//获取对应的查询语句
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
//获取查询所需要的参数的列表
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
// check for nested result maps in parameter mappings (issue #30)
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;
}
}
走来先获取对应的查询列表和查询所需要的参数的列表,如果查询的所需要的参数为空的话,就不需要绑定参数了,直接创建BoundSql
对象。如果不为空,那么就需要遍历对应的参数。获取对应的ResultMap
,很显然这儿获取的是空的。然后返回到最初的执行的代码地方,会执行查询缓存的方法,这儿不讲,后面会写一篇博客讲,然后会执行query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
方法,我们继续跟进代码,具体的代码如下:
public class CachingExecutor implements Executor {
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//二级缓存的Cache
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
}
走来先获取缓存中的数据,很明显这儿是第一次查询,所以缓存中数据也是空,所以会执行下面的方法delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
具体的代码如下:
public abstract class BaseExecutor implements Executor {
@Override
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.");
}
//判断queryStack是否等于0,判断是否需要刷新缓存,都是前面判断的,如果是select就不需要更新缓存
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;
}
}
上面判断数据连接是否关闭,同时判断queryStack
是否等于0
和判断是否要刷新缓存,如果都成立的话,直接调用clearLocalCache();
方法清楚缓存。然后从缓存中去这个查询的看看有没有值,很明显这儿是空,然后会执行queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
方法,具体的代码如下:
public abstract class BaseExecutor implements Executor {
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;
}
}
走来先将这个key
存入缓存,占一个键,然后执行doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
方法,具体的代码如下,执行完后先清楚,然后再将查询到list
存入到缓存中去。
public class SimpleExecutor extends BaseExecutor {
@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.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
}
这个时候终于看到了jdbc
代码了,那么具体的参数的绑定在什么地方呢?让我们继续看prepareStatement(handler, ms.getStatementLog());
方法中代码,具体如下:
public class SimpleExecutor extends BaseExecutor {
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;
}
}
public class RoutingStatementHandler implements StatementHandler {
@Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
}
public class PreparedStatementHandler extends BaseStatementHandler {
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
}
public class DefaultParameterHandler implements ParameterHandler {
@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;
//propertyName=id
String propertyName = parameterMapping.getProperty();
//判断是否有附加参数
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) { //值为1
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 jdbcType = parameterMapping.getJdbcType();
//如果两个值都为空的话,设置jdbcType类型为OTHER
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
//调用底层的JDBC设置值的方法
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
}
上面将参数的值取出来,进行一系列处理,最终调用typeHandler.setParameter(ps, i + 1, value, jdbcType);
方法,这个方法就是调用底层JDBC
的设置的方法。至此参数的绑定结束。最后执行query
方法。具体代码如下:
public class RoutingStatementHandler implements StatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.query(statement, resultHandler);
}
}
public class PreparedStatementHandler extends BaseStatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//调用JDBC的底层的查询操作
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
}
我们终于看到了JDBC
的底层调用了,这个时候还剩一个结果集的封装,这个时候我们需要看resultSetHandler.handleResultSets(ps);
方法,具体的代码如下:
public class DefaultResultSetHandler implements ResultSetHandler {
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
//创建ResultSet的包装类
ResultSetWrapper rsw = getFirstResultSet(stmt);
//获取对应的resultMaps
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
//校验对应的长度
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
//获取对应的resultMap
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);
}
}
上面的代码进过一系列的调用最终会调用handleResultSet(rsw, resultMap, multipleResults, null);
方法,具体的代码如下:
public class DefaultResultSetHandler implements ResultSetHandler {
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
//这个不会执行,由于传进来的就是null
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
//第一次进来为空
if (resultHandler == null) {
//创建DefaultResultHandler
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());
}
}
}
然后会执行handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
方法,具体的代码如下:
public class DefaultResultSetHandler implements ResultSetHandler {
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(rsw, resultMap, resultHandler, rowBounds, parentMapping);
方法,我们继续跟进去查看代码,具体代码如下:
public class DefaultResultSetHandler implements ResultSetHandler {
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
//创建默认的DefaultResultContext
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
//获取对应ResultSet对象
ResultSet resultSet = rsw.getResultSet();
//分页跳过
skipRows(resultSet, rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
}
上面的代码会执行resolveDiscriminatedResultMap(resultSet, resultMap, null);
方法,具体的代码如下:
public class DefaultResultSetHandler implements ResultSetHandler {
public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
Set<String> pastDiscriminators = new HashSet<>();
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;
}
}
然后返回resultMap
,这个时候会执行getRowValue(rsw, discriminatedResultMap, null);
方法,将查询到的列和值封装成一个hashMap
返回。然后执行storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
方法。具体的代码如下:
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
if (parentMapping != null) {
linkToParents(rs, parentMapping, rowValue);
} else {
callResultHandler(resultHandler, resultContext, rowValue);
}
}
进行了一些的数据的封装,然后返回,这个时候这个结果集就被包装成指定的对象,并且存入到multipleResults
变量中去。然后关闭结果集,有点类似JDBC
中关闭结果集。让我们返回到最初代码执行的地方,具体的代码如下:
public class DefaultResultSetHandler implements ResultSetHandler {
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
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++;
}
//获取resultSets
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);
}
}
这个时候这个while
执行结束了,然后获取resultSets
的值为null
,这个时候下面的if
判断不会执行,然后执行collapseSingleResultList(multipleResults);
方法,将刚才包装好的结果集返回。然后返回到执行的代码的地方,这个时候会执行关闭Statement
的代码,也是JDBC
中的代码。最终返回到如下到代码地方。
public abstract class BaseExecutor implements Executor {
@Override
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.");
}
//判断queryStack是否等于0,判断是否需要刷新缓存,都是前面判断的,如果是select就不需要更新缓存
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 {
//查询到栈减1
queryStack--;
}
if (queryStack == 0) { //=0
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
}
至此整个查询就结束了,就直接返回了。
我们再来看第二种的语句的执行流程,主要是$和动态标签,在这我们不会详细的介绍,这儿只会介绍不同的地方。
由于刚才我们传入的参数只有一个,所以直接返回一个,这儿有两个参数,所以这儿参数的转换是不同,具体的代码如下:
public class ParamNameResolver {
public Object getNamedParams(Object[] args) {
//获取参数的长度,这儿的长度是2
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {//判断参数的长度是不是1同时也没有加上注解
return args[names.firstKey()];//这个是我们上一个满足的条件,直接返回参数名的值
} else {
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
//names : arg0,arg1
// key:arg0 value:Bob;key:arg1 value:Ruby
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
//key:param1 value:Bob; key:param value:Ruby
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
}
上面的代码判断参数的长度是不是1,如果是1,直接将这个值进行返回,也不用键,因为只有一个值,所以不存在对应的键和对应的值。如果是多个参数,就会动态的生成。例如我们这儿传入的参数是字符串Bob
和字符串Ruby
,这儿就会往Map
中添加key:arg0,value:Bob;key:arg1,value:Ruby;key:param1,value:Bob;key:param2,value:Ruby
。
由于第二个查询的语句有动态的查询的语句,所以当中getBoundSql
方法的执行的流程和原来的静态的代码不一样,具体调用的代码如下:
public class DynamicSqlSource implements SqlSource {
@Override
public BoundSql getBoundSql(Object parameterObject) {
//获取对应的参数
DynamicContext context = new DynamicContext(configuration, parameterObject);
//调用执行apply的方法
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
context.getBindings().forEach(boundSql::setAdditionalParameter);
return boundSql;
}
}
上面的代码走来会获取指定的参数,然后调用apply
方法。这个时候会对动态标签中的数据进行解析,同时也会讲if
标签中的参数也给封装进去,最后的SQL
的语句如下:
select * from mybooks
WHERE 1=1
and author = Bob
and title = #{param2}
所以可以得知$
标签中参数会在这儿替换掉。然后会执行sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
方法将#{}
替换成?
,同时将这个要绑定的参数的值封装到这个对象中去。具体的代码如下:
public class SqlSourceBuilder extends BaseBuilder {
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
String sql = parser.parse(originalSql);
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
}
这个时候SQL语句就会变成下面这样
select * from mybooks
WHERE 1=1
and author = Bob
and title = ?
后面的执行操作就是一样的了。下篇博客会讲下执行流程中增删改。由于这篇博客没有参数names在哪生成的。后面的博客后有讲到。