前言
前面我们从源码层面梳理了 SqlSessionFactory、SqlSession 的创建过程及 Mapper 获取过程,本篇我们继续分析一下 Mapper 接口方法的执行的过程,也就是 SQL 的执行流程。
Mybatis 相关知识传送门
初识 MyBatis 【MyBatis 核心概念】
MyBatis 源码分析–SqlSessionFactory
MyBatis 源码分析–获取SqlSession
MyBatis 源码分析-- getMapper(获取Mapper)
SQL 请求执行流程
分析 SQL 执行流程之前,先附上一张简单的源码调用链路图,方便后面分析 SQL 的执行流程。
执行 Mapper 方法
前面分析了,从 SqlSession 中获取的 Mapper 是代理对象,执行 Mapper 接口方法时候,会调用 MapperProxy#invoke 方法。
MapperProxy#invoke 方法源码分析
MapperProxy#invoke 方法会判断执行的方法是否是 Object 类的方法,如果是就无需代理,否则才会走代理的方法,代理的方法有两个默认实现 一个是普通方法 PlainMethodInvoker 和默认方法实现 DefaultMethodInvoker。
//org.apache.ibatis.binding.MapperProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {//判断是不是 Object 类的方法 是就直接返回 无需代理 不是则调用缓存的 invoke 方法 有两个实现类 一个是普通方法 PlainMethodInvoker 和默认方法实现 DefaultMethodInvokerreturn Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);} catch (Throwable var5) {throw ExceptionUtil.unwrapThrowable(var5);}
}
MapperProxy#cachedInvoker 方法源码分析
MapperProxy#cachedInvoker 方法会先从缓存中获取 MapperMethodInvoker,如果缓存不存在则穿件一个 MapperMethodInvoker 再加入缓存,创建 MapperMethodInvoker 时候会判断是普通方法还是默认方法。
//org.apache.ibatis.binding.MapperProxy#cachedInvoker
private MapperProxy.MapperMethodInvoker cachedInvoker(Method method) throws Throwable {try {//获取 MapperMethodInvokerMapperProxy.MapperMethodInvoker invoker = (MapperProxy.MapperMethodInvoker)this.methodCache.get(method);//缓存中是否存在 不存在 则加入缓存return invoker != null ? invoker : (MapperProxy.MapperMethodInvoker)this.methodCache.computeIfAbsent(method, (m) -> {//是否是默认的if (m.isDefault()) {try {//默认的方法return privateLookupInMethod == null ? new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava8(method)) : new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava9(method));} catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException var4) {throw new RuntimeException(var4);}} else {//普通方法 new MapperMethod 重点关注return new MapperProxy.PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));}});} catch (RuntimeException var4) {Throwable cause = var4.getCause();throw (Throwable)(cause == null ? var4 : cause);}
}
MapperMethod 创建源码分析
MapperMethod 的创建中做了两个操作,一个是创建了 SqlCommand,还有一个是创建了MethodSignature。
//org.apache.ibatis.binding.MapperMethod#MapperMethod
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {//创建 SqlCommandthis.command = new MapperMethod.SqlCommand(config, mapperInterface, method);//创建 MethodSignaturethis.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}
SqlCommand 创建源码分析
SqlCommand 的创建其实是从 MappedStatement 中获取 name、SqlCommandType 完成赋值,从这里我们可以看出 MappedStatement 中存储的是 SQL 信息,例如:id、SELECT、UPDATE、DELETE 等 。
//org.apache.ibatis.binding.MapperMethod.SqlCommand#SqlCommand
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {//获取方法名称String methodName = method.getName();//获取方法的 classClass<?> declaringClass = method.getDeclaringClass();//得到 MappedStatementMappedStatement ms = this.resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration);//MappedStatement 为空判断if (ms == null) {//是否是 Flush 注解标记的接口if (method.getAnnotation(Flush.class) == null) {//没有找到对应的 statementthrow new BindingException("Invalid bound statement (not found): " + mapperInterface.getName() + "." + methodName);}this.name = null;//SqlCommandType FLUSHthis.type = SqlCommandType.FLUSH;} else {//有 MappedStatement this.name = ms.getId();this.type = ms.getSqlCommandType();//SqlCommandType.UNKNOWN 判断if (this.type == SqlCommandType.UNKNOWN) {throw new BindingException("Unknown execution method for: " + this.name);}}}//org.apache.ibatis.binding.MapperMethod.SqlCommand#resolveMappedStatement
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration) {//接口类名 + . 方法名String statementId = mapperInterface.getName() + "." + methodName;//在 mappedStatements 中是否存在 解析xml 时候存入的if (configuration.hasStatement(statementId)) {//找到返回return configuration.getMappedStatement(statementId);} else if (mapperInterface.equals(declaringClass)) {return null;} else {Class[] var6 = mapperInterface.getInterfaces();int var7 = var6.length;//查询父接口for(int var8 = 0; var8 < var7; ++var8) {Class<?> superInterface = var6[var8];if (declaringClass.isAssignableFrom(superInterface)) {MappedStatement ms = this.resolveMappedStatement(superInterface, methodName, declaringClass, configuration);if (ms != null) {return ms;}}}return null;}
}
SqlCommandType
public enum SqlCommandType {UNKNOWN,INSERT,UPDATE,DELETE,SELECT,FLUSH;private SqlCommandType() {}
}
MethodSignature#MethodSignature 源码分析
MethodSignature#MethodSignature 方法构造了方法签名,完成 MethodSignature 的初始化,方法签名初始化完成之后,MapperMethod 实例就创建完成了,回到了 MapperProxy 的 invoke 方法,准备开始执行 execute 方法。
//org.apache.ibatis.binding.MapperMethod.MethodSignature#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();}//返回voidthis.returnsVoid = Void.TYPE.equals(this.returnType);//返回多条this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();//返回游标this.returnsCursor = Cursor.class.equals(this.returnType);//是否返回Optionalthis.returnsOptional = Optional.class.equals(this.returnType);//mapKey的值this.mapKey = this.getMapKey(method);//是否map查询this.returnsMap = this.mapKey != null;//RowBounds 索引 RowBounds 是分页用的 记录 RowBounds 是第几个参数 this.rowBoundsIndex = this.getUniqueParamIndex(method, RowBounds.class);//ResultHandler 索引 resultHandler 是结果处理器 ResultHandler 是第几个参数this.resultHandlerIndex = this.getUniqueParamIndex(method, ResultHandler.class);//参数名称解析器this.paramNameResolver = new ParamNameResolver(configuration, method);
}
MapperProxy.PlainMethodInvoker 源码分析
前面我们分析了 MapperProxy.PlainMethodInvoker 包装了 MapperMethod,PlainMethodInvoker 的 invoke 方法最终会调用 MapperMethod 的 invoke 方法,我们接着分析 MapperMethod#execute 方法。
private static class PlainMethodInvoker implements MapperProxy.MapperMethodInvoker {private final MapperMethod mapperMethod;public PlainMethodInvoker(MapperMethod mapperMethod) {this.mapperMethod = mapperMethod;}public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {//调用 MapperMethod#execute 方法return this.mapperMethod.execute(sqlSession, args);}
}
MapperMethod#execute 源码分析
MapperMethod#execute 方法会根据不用的 Type 和不同的返回值类型,调用不同的方法执行 SQL。
//org.apache.ibatis.binding.org.apache.ibatis.binding.MapperMethod#execute
public Object execute(SqlSession sqlSession, Object[] args) {//返回结果Object result;//参数Object param;//sql 语句类型 insert update delete selectswitch(this.command.getType()) {case INSERT://解析参数param = this.method.convertArgsToSqlCommandParam(args);//rowCountResult 包装返回结果 sqlSession.insert 调用的是 DefauleSqlSession 的inset 方法 最终调用的的是 executor 的 update方法result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));break;case UPDATE:param = this.method.convertArgsToSqlCommandParam(args);result = this.rowCountResult(sqlSession.update(this.command.getName(), param));break;case DELETE:param = this.method.convertArgsToSqlCommandParam(args);result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));break;case SELECT://返回值类型判断if (this.method.returnsVoid() && this.method.hasResultHandler()) {//没有返回值this.executeWithResultHandler(sqlSession, args);result = null;} else if (this.method.returnsMany()) {//返回多个result = this.executeForMany(sqlSession, args);} else if (this.method.returnsMap()) {//返回 Mapresult = this.executeForMap(sqlSession, args);} else if (this.method.returnsCursor()) {//返回游标result = this.executeForCursor(sqlSession, args);} else {//返回值为 单一对象的方法//将方法参数转换为 SQL 参数param = this.method.convertArgsToSqlCommandParam(args);//selectOne 语句的执行入口result = sqlSession.selectOne(this.command.getName(), param);if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {result = Optional.ofNullable(result);}}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + this.command.getName());}//返回值判断if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");} else {return result;}
}//org.apache.ibatis.session.defaults.DefaultSqlSession#insert(java.lang.String, java.lang.Object)
public int insert(String statement, Object parameter) {return this.update(statement, parameter);
}//org.apache.ibatis.session.defaults.DefaultSqlSession#update(java.lang.String, java.lang.Object)
public int update(String statement, Object parameter) {int var4;try {this.dirty = true;MappedStatement ms = this.configuration.getMappedStatement(statement);var4 = this.executor.update(ms, this.wrapCollection(parameter));} catch (Exception var8) {throw ExceptionFactory.wrapException("Error updating database. Cause: " + var8, var8);} finally {ErrorContext.instance().reset();}return var4;
}
MethodSignature#convertArgsToSqlCommandParam 源码分析
MethodSignature#convertArgsToSqlCommandParam 方法主要是对参数进行解析。
//org.apache.ibatis.binding.MapperMethod.MethodSignature#convertArgsToSqlCommandParam
public Object convertArgsToSqlCommandParam(Object[] args) {return this.paramNameResolver.getNamedParams(args);
}//org.apache.ibatis.reflection.ParamNameResolver#getNamedParams
public Object getNamedParams(Object[] args) {int paramCount = this.names.size();if (args != null && paramCount != 0) {if (!this.hasParamAnnotation && paramCount == 1) {//一个参数Object value = args[(Integer)this.names.firstKey()];return wrapToMapIfCollection(value, this.useActualParamName ? (String)this.names.get(0) : null);} else {//多个参数Map<String, Object> param = new ParamMap();int i = 0;//遍历加入 Mapfor(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {Entry<Integer, String> entry = (Entry)var5.next();param.put(entry.getValue(), args[(Integer)entry.getKey()]);//通用参数名String genericParamName = "param" + (i + 1);if (!this.names.containsValue(genericParamName)) {//不含有通用参数名才添加param.put(genericParamName, args[(Integer)entry.getKey()]);}}return param;}} else {//没有参数return null;}
}//处理集合参数
//org.apache.ibatis.reflection.ParamNameResolver#wrapToMapIfCollection
public static Object wrapToMapIfCollection(Object object, String actualParamName) {ParamMap map;//是否是集合类型if (object instanceof Collection) {map = new ParamMap();map.put("collection", object);if (object instanceof List) {map.put("list", object);}Optional.ofNullable(actualParamName).ifPresent((name) -> {map.put(name, object);});return map;} else if (object != null && object.getClass().isArray()) {//是否 array 参数map = new ParamMap();map.put("array", object);Optional.ofNullable(actualParamName).ifPresent((name) -> {map.put(name, object);});return map;} else {//非集合数组参数直接返回return object;}
}
DefaultSqlSession#selectOne 源码分析
DefaultSqlSession#selectOne 方法实际调用的也是 selectList 方法。
//org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)
public <T> T selectOne(String statement, Object parameter) {//调用的也是 selectListList<T> 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;}
}
DefaultSqlSession#selectList 源码分析
DefaultSqlSession#selectList 方法调用了执行器 Executor 执行 SQL 语句。
//org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object)
public <E> List<E> selectList(String statement, Object parameter) {//返回结果的最大值 int 默认值 2147483647return this.selectList(statement, parameter, RowBounds.DEFAULT);
}//DefaultSqlSession#selectList 方法调用了执行器 Executor 执行 SQL 语句。
//org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {List var6;try {//获取 MappedStatement MappedStatement 中有已经处理的 sql 信息MappedStatement ms = this.configuration.getMappedStatement(statement);//最终调用执行器的 query RowBounds 是用来逻辑分页 按照条件将数据从数据库查询到内存中 在内存中进行分页 wrapCollection 处理集合或者数组参数List<E> result = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);var6 = result;} catch (Exception var10) {throw ExceptionFactory.wrapException("Error querying database. Cause: " + var10, var10);} finally {ErrorContext.instance().reset();}return var6;
}
CachingExecutor#query 源码分析
CachingExecutor#query 方法会从 MappedStatement 中获取 SQL 信息,创建缓存 key 并执行查询,Configuration 中 cacheEnabled 属性值默认为 true,因此会执行 CachingExecutor 的 query方法。
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {//获取 SQL 基本信息BoundSql boundSql = ms.getBoundSql(parameterObject);//创建缓存 keyCacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);//执行查询return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
CachingExecutor#query 源码分析
CachingExecutor#query 会判断是否使用了缓存,如果允许使用缓存会先从二级缓存查询,二级缓存中查询不到才会去查询一级缓存或者数据库,如果二级缓存为空或者不允许使用缓存就会直接去查询一级缓存或者数据库。
//org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {//获取 二级缓存Cache cache = ms.getCache();//为空判断if (cache != null) {//是否刷新缓存//select 时候 flushCacheRequired 默认是false 不清除二级缓存 //insert update delete 时候 flushCacheRequired 是 true 会清除二级缓存this.flushCacheIfRequired(ms);//如果使用了缓存 ResultHandler 不为空 if (ms.isUseCache() && resultHandler == null) {//对存储过程的处理 确保没有存储过程 如果有存储过程 就报错this.ensureNoOutParams(ms, parameterObject, boundSql);//从二级缓存中查询数据List<E> list = (List)this.tcm.getObject(cache, key);//结果为空 判断if (list == null) {//为空 BaseExexutor 执行查询数据库list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);//加入二级缓存this.tcm.putObject(cache, key, list);}return list;}}//缓存为空 BaseExexutor 直接查询数据库return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
BaseExecutor#query 源码分析
BaseExecutor#query 方法会先去一级缓存查询数据,一级缓存中查询不到数据才会去查询数据库的数据,同时也会做一些清除一级缓存的判断。
//org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.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 (this.closed) {throw new ExecutorException("Executor was closed.");} else {//queryStack 查询堆栈 防止递归查询重复处理缓存 是否刷新缓存 flushCacheRequired 是 true 清除一级缓存if (this.queryStack == 0 && ms.isFlushCacheRequired()) {//清除一级缓存this.clearLocalCache();}List list;try {//查询堆栈 +1++this.queryStack;//从一级缓存中获取数据list = resultHandler == null ? (List)this.localCache.getObject(key) : null;if (list != null) {//从一级缓存中获取数据this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {//从数据库查询数据list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {//查询堆栈-1--this.queryStack;}if (this.queryStack == 0) {Iterator i$ = this.deferredLoads.iterator();while(i$.hasNext()) {BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();deferredLoad.load();}this.deferredLoads.clear();if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {this.clearLocalCache();}}return list;}
}
BaseExecutor#queryFromDatabase 源码分析
BaseExecutor#queryFromDatabase 方法会去查询数据库,并将查询结果加入一级缓存。
//org.apache.ibatis.executor.BaseExecutor#queryFromDatabase
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {//一级缓存 存储一个占位符this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);List list;try {//执行数据库查询list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {//清除占位符缓存this.localCache.removeObject(key);}//存入一级缓存this.localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {this.localOutputParameterCache.putObject(key, parameter);}return list;
}
SimpleExecutor#doQuery 源码分析
SimpleExecutor#doQuery 方法通过 MappedStatement(SQL语句信息,包含 SQL、参数、返回值等)获取 Configuration,创建 StatementHandler(封装 JDBC 操作)。
//org.apache.ibatis.executor.SimpleExecutor#doQuery
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;List var9;try {//获取 Configuration 对象 Configuration configuration = ms.getConfiguration();//创建 StatementHandler 对象StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);//获取 Statementstmt = this.prepareStatement(handler, ms.getStatementLog());//执行查询var9 = handler.query(stmt, resultHandler);} finally {//关闭 statementthis.closeStatement(stmt);}return var9;
}
SimpleExecutor#prepareStatement 源码分析
SimpleExecutor#prepareStatement 方法获取了数据库连接,创建 Statement,通过 StatementHandler 处理相关参数,最终返回 Statement。
//org.apache.ibatis.executor.SimpleExecutor#prepareStatement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {//获取数据库连接 会从事务管理器中获取连接Connection connection = this.getConnection(statementLog);//创建 Statement PreparedStatementStatement stmt = handler.prepare(connection);//通过 ParameterHandler 设置参数handler.parameterize(stmt);return stmt;
}
PreparedStatementHandler#query 源码分析
PreparedStatementHandler#query 方法通过 PreparedStatement 执行查询,ResultHandler 获取结果集,至此,一个简单的 SQL 查询结束了。
//org.apache.ibatis.executor.statement.PreparedStatementHandler#query
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement)statement;//执行ps.execute();//ResultHandler 获取结果集return this.resultSetHandler.handleResultSets(ps);
}
至此,selectOne 的执行过程分析结束,其他查询的执行过程大同小异。
欢迎提出建议及对错误的地方指出纠正。