上一篇文章https://blog.csdn.net/yange1025/article/details/84709273介绍了如何在Spring项目中配置mybatis以及mybatis的启动过程。本篇文章将从用户发起一笔查询到返回查询结果来介绍mybatis的执行流程。
入口从用户从Spring容器获取Mapper接口对应的bean,并调用接口方法执行查询开始。我们知道从Spring容器获取的Mapper bean是通过动态代理生成的,其所有方法的调用都会转嫁到MapperProxy的invoke方法
1 org.apache.ibatis.binding.MapperProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 如果是Object类中声明的方法,则直接反射调用该方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
// 如果是接口中的default方法,则通过Java动态语言支持MethodHandle调用
else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 其他的是Mapper接口方法的实现
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
1.1 org.apache.ibatis.binding.MapperMethod#execute
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// command的name是mapper接口的方法名,如:com.my.UserMapper.retrieve
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()) { // 返回Map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) { // 游标查询
result = executeForCursor(sqlSession, args);
} else { // 单条记录查询
// 将方法参数转换成sql参数
Object param = method.convertArgsToSqlCommandParam(args);
// 调用sqlSession执行查询
result = sqlSession.selectOne(command.getName(), param);
}
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()) {