diff --git a/docs/Mybatis/Mybatis-MapperMethod.md b/docs/Mybatis/Mybatis-MapperMethod.md new file mode 100644 index 0000000..206d331 --- /dev/null +++ b/docs/Mybatis/Mybatis-MapperMethod.md @@ -0,0 +1,200 @@ +# MapperMethod +- 源码地址: `org.apache.ibatis.binding.MapperMethod`,核心方法是`execute` + +```java + /** + * CRUD 不同的执行处理 + * + * @param sqlSession + * @param args + * @return + */ + 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; + } + +``` + +```java + /** + * 根据 resultHandler 进行处理 + * + * @param sqlSession + * @param args + */ + private void executeWithResultHandler(SqlSession sqlSession, Object[] args) { + MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName()); + if (!StatementType.CALLABLE.equals(ms.getStatementType()) + && void.class.equals(ms.getResultMaps().get(0).getType())) { + throw new BindingException("method " + command.getName() + + " needs either a @ResultMap annotation, a @ResultType annotation," + + " or a resultType attribute in XML so a ResultHandler can be used as a parameter."); + } + Object param = method.convertArgsToSqlCommandParam(args); + // 判断是否有 RowBounds + if (method.hasRowBounds()) { + RowBounds rowBounds = method.extractRowBounds(args); + sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args)); + } else { + sqlSession.select(command.getName(), param, method.extractResultHandler(args)); + } + } + +``` + + +- 返回值为多个的情况 +```java + /** + * 针对多个查询结果进行 ,转换成不同的 list 或者数组 + * + * @param sqlSession + * @param args + * @param + * @return + */ + private Object executeForMany(SqlSession sqlSession, Object[] args) { + List result; + Object param = method.convertArgsToSqlCommandParam(args); + if (method.hasRowBounds()) { + RowBounds rowBounds = method.extractRowBounds(args); + // 直接 list + 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()) { + // 转换成 array + return convertToArray(result); + } else { + // 转换成 collection + return convertToDeclaredCollection(sqlSession.getConfiguration(), result); + } + } + return result; + } + +``` + +### convertToArray +```java + /** + * 转换为数组 + * + * @param list 数据库查询结果 + * @param + * @return + */ + @SuppressWarnings("unchecked") + private Object convertToArray(List list) { + // 获取返回类型 + Class arrayComponentType = method.getReturnType().getComponentType(); + // new 一个 array + Object array = Array.newInstance(arrayComponentType, list.size()); + if (arrayComponentType.isPrimitive()) { + for (int i = 0; i < list.size(); i++) { + Array.set(array, i, list.get(i)); + } + return array; + } else { + // 通过 toArray方法转换 + return list.toArray((E[]) array); + } + } + +``` +### convertToDeclaredCollection +```java + /** + * 转换为不同的list对象 + * + * @param config + * @param list 数据库查询结果 + * @param + * @return + */ + private Object convertToDeclaredCollection(Configuration config, List list) { + // mybatis ObjectFactory 创建mapper 的返回结果对象 + Object collection = config.getObjectFactory().create(method.getReturnType()); + MetaObject metaObject = config.newMetaObject(collection); + // metaObject.objectWrapper => CollectionWrapper + // MetaObject 对象的 objectWrapper 现在是 CollectionWrapper 它是 Collection 的包装 + metaObject.addAll(list); + return collection; + } + +``` + + +- 上述两个为转换的过程,其实质还是在 `org.apache.ibatis.session.SqlSession` 中做执行操作 + + + +## debug +- 修改mapper返回数组对`org.apache.ibatis.binding.MapperMethod#convertToArray`方法进行测试 +```java + HsSell[] list(@Param("ID") Integer id); +``` + +![image-20191219092442456](/images/mybatis/image-20191219092442456.png) + +- 修改mapper,对`org.apache.ibatis.binding.MapperMethod#convertToDeclaredCollection`进行测试 + + ```java + LinkedList list(@Param("ID") Integer id); + ``` + + + +![image-20191219093043035](/images/mybatis/image-20191219093043035.png) + + + diff --git a/images/mybatis/image-20191219092442456.png b/images/mybatis/image-20191219092442456.png new file mode 100644 index 0000000..b256653 Binary files /dev/null and b/images/mybatis/image-20191219092442456.png differ diff --git a/images/mybatis/image-20191219093043035.png b/images/mybatis/image-20191219093043035.png new file mode 100644 index 0000000..2863e95 Binary files /dev/null and b/images/mybatis/image-20191219093043035.png differ