从 <MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory> 文中,我们知道Mapper,通过MapperProxy代理类执行他的接口方法,当mapper方法被调用的时候对应的MapperProxy会生成相应的MapperMethod并且会缓存起来,这样当多次调用同一个mapper方法时候只会生成一个MapperMethod,提高了时间和内存效率:

//这里会拦截Mapper接口的所有方法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (Object.class.equals(method.getDeclaringClass())) { //如果是Object中定义的方法,直接执行。如toString(),hashCode()等try {return method.invoke(this, args);//
      } catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}final MapperMethod mapperMethod = cachedMapperMethod(method);  //其他Mapper接口定义的方法交由mapperMethod来执行return mapperMethod.execute(sqlSession, args);}

最后2句关键,我们执行所调用Mapper的每一个接口方法,最后返回的是MapperMethod.execute方法。每一个MapperMethod对应了一个mapper文件中配置的一个sql语句或FLUSH配置,对应的sql语句通过mapper对应的class文件名+方法名从Configuration对象中获得。

我们看一下MapperMethod的源码:

package org.apache.ibatis.binding;import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;
/** * MapperMethod代理Mapper所有方法 */
public class MapperMethod {//一个内部封 封装了SQL标签的类型 insert update delete selectprivate final SqlCommand command;//一个内部类 封装了方法的参数信息 返回类型信息等 private final MethodSignature method;public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {this.command = new SqlCommand(config, mapperInterface, method);this.method = new MethodSignature(config, method);}
  /**   * 这个方法是对SqlSession的包装,对应insert、delete、update、select四种操作   */public Object execute(SqlSession sqlSession, Object[] args) {    Object result;//返回结果   //INSERT操作    if (SqlCommandType.INSERT == command.getType()) {      //处理参数      Object param = method.convertArgsToSqlCommandParam(args);//调用sqlSession的insert方法       result = rowCountResult(sqlSession.insert(command.getName(), param));    } else if (SqlCommandType.UPDATE == command.getType()) {      //UPDATE操作 同上      Object param = method.convertArgsToSqlCommandParam(args);      result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { //DELETE操作 同上 Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) { //如果返回void 并且参数有resultHandler  ,则调用 void select(String statement, Object parameter, ResultHandler handler);方法   if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) {//如果返回多行结果,executeForMany这个方法调用 <E> List<E> selectList(String statement, Object parameter);    result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { //如果返回类型是MAP 则调用executeForMap方法  result = executeForMap(sqlSession, args); } else { //否则就是查询单个对象 Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else { //接口方法没有和sql命令绑定 throw new BindingException("Unknown execution method for: " + command.getName()); }
    //如果返回值为空 并且方法返回值类型是基础类型 并且不是VOID 则抛出异常   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; } private Object rowCountResult(int rowCount) { final Object result; if (method.returnsVoid()) { result = null; } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) { result = rowCount; } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) { result = (long) rowCount; } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) { result = (rowCount > 0); } else { throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType()); } return result; } private void executeWithResultHandler(SqlSession sqlSession, Object[] args) { MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName()); if (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); 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)); } } //返回多行结果 调用sqlSession.selectList方法  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { List<E> result; Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds()) {//如果参数含有rowBounds则调用分页的查询  RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<E>selectList(command.getName(), param, rowBounds); } else {//没有分页则调用普通查询  result = sqlSession.<E>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; } private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) { Object collection = config.getObjectFactory().create(method.getReturnType()); MetaObject metaObject = config.newMetaObject(collection); metaObject.addAll(list); return collection; } @SuppressWarnings("unchecked") private <E> E[] convertToArray(List<E> list) { E[] array = (E[]) Array.newInstance(method.getReturnType().getComponentType(), list.size()); array = list.toArray(array); return array; } private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) { Map<K, V> result; Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds); } else { result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey()); } return result; } public static class ParamMap<V> extends HashMap<String, V> { private static final long serialVersionUID = -2212268410512043556L; @Override public V get(Object key) { if (!super.containsKey(key)) { throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet()); } return super.get(key); } }  //封装了具体执行的动作 public static class SqlCommand { //xml标签的id  private final String name;//insert update delete select的具体类型  private final SqlCommandType type; public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) throws BindingException { //拿到全名 比如 org.mybatis.example.UserMapper.selectByPrimaryKey String statementName = mapperInterface.getName() + "." + method.getName(); //MappedStatement对象,封装一个Mapper接口对应的sql操作  MappedStatement ms = null; if (configuration.hasStatement(statementName)) { //从Configuration对象查找是否有这个方法的全限定名称,如果有则根据方法的全限定名称获取MappedStatement ms = configuration.getMappedStatement(statementName); } else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // issue #35 //如果没有在Configuration对象中找到这个方法,则向上父类中获取全限定方法名 String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName(); if (configuration.hasStatement(parentStatementName)) { ms = configuration.getMappedStatement(parentStatementName); } } if (ms == null) { throw new BindingException("Invalid bound statement (not found): " + statementName); } //这个ms.getId,其实就是我们在mapper.xml配置文件中配置一条sql语句设置的id属性的值 name = ms.getId(); //sql的类型(insert、update、delete、select) type = ms.getSqlCommandType(); if (type == SqlCommandType.UNKNOWN) { //判断SQL标签类型 未知就抛异常 throw new BindingException("Unknown execution method for: " + name); } } public String getName() { return name; } public SqlCommandType getType() { return type; } }  /** * 方法签名,封装了接口当中方法的 参数类型 返回值类型 等信息 */ public static class MethodSignature { private final boolean returnsMany;//是否返回多条结果  private final boolean returnsMap; //返回值是否是MAP  private final boolean returnsVoid;//返回值是否是VOID  private final Class<?> returnType; //返回值类型  private final String mapKey; private final Integer resultHandlerIndex;//resultHandler类型参数的位置  private final Integer rowBoundsIndex; //rowBound类型参数的位置 private final SortedMap<Integer, String> params;//用来存放参数信息 private final boolean hasNamedParameters;  //是否存在命名参数  public MethodSignature(Configuration configuration, Method method) throws BindingException { this.returnType = method.getReturnType(); this.returnsVoid = void.class.equals(this.returnType); this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray()); this.mapKey = getMapKey(method); this.returnsMap = (this.mapKey != null); this.hasNamedParameters = hasNamedParams(method); this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters)); }  /** * 创建SqlSession对象需要传递的参数逻辑 * args是用户mapper所传递的方法参数列表, 如果方法没有参数,则返回null.   * 如果方法只包含一个参数并且不包含命名参数, 则返回传递的参数值。 * 如果包含多个参数或包含命名参数,则返回包含名字和对应值的map对象、 */ public Object convertArgsToSqlCommandParam(Object[] args) { final int paramCount = params.size(); if (args == null || paramCount == 0) { return null; } else if (!hasNamedParameters && paramCount == 1) { return args[params.keySet().iterator().next()]; } else { final Map<String, Object> param = new ParamMap<Object>(); int i = 0; for (Map.Entry<Integer, String> entry : params.entrySet()) { param.put(entry.getValue(), args[entry.getKey()]); // issue #71, add param names as param1, param2...but ensure backward compatibility final String genericParamName = "param" + String.valueOf(i + 1); if (!param.containsKey(genericParamName)) { param.put(genericParamName, args[entry.getKey()]); } i++; } return param; } } public boolean hasRowBounds() { return (rowBoundsIndex != null); } public RowBounds extractRowBounds(Object[] args) { return (hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null); } public boolean hasResultHandler() { return (resultHandlerIndex != null); } public ResultHandler extractResultHandler(Object[] args) { return (hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null); } public String getMapKey() { return mapKey; } public Class<?> getReturnType() { return returnType; } public boolean returnsMany() { return returnsMany; } public boolean returnsMap() { return returnsMap; } public boolean returnsVoid() { return returnsVoid; } private Integer getUniqueParamIndex(Method method, Class<?> paramType) { Integer index = null; final Class<?>[] argTypes = method.getParameterTypes(); for (int i = 0; i < argTypes.length; i++) { if (paramType.isAssignableFrom(argTypes[i])) { if (index == null) { index = i; } else { throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters"); } } } return index; } private String getMapKey(Method method) { String mapKey = null; if (Map.class.isAssignableFrom(method.getReturnType())) { final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class); if (mapKeyAnnotation != null) { mapKey = mapKeyAnnotation.value(); } } return mapKey; } private SortedMap<Integer, String> getParams(Method method, boolean hasNamedParameters) { final SortedMap<Integer, String> params = new TreeMap<Integer, String>(); final Class<?>[] argTypes = method.getParameterTypes(); for (int i = 0; i < argTypes.length; i++) { if (!RowBounds.class.isAssignableFrom(argTypes[i]) && !ResultHandler.class.isAssignableFrom(argTypes[i])) { String paramName = String.valueOf(params.size()); if (hasNamedParameters) { paramName = getParamNameFromAnnotation(method, i, paramName); } params.put(i, paramName); } } return params; } private String getParamNameFromAnnotation(Method method, int i, String paramName) { final Object[] paramAnnos = method.getParameterAnnotations()[i]; for (Object paramAnno : paramAnnos) { if (paramAnno instanceof Param) { paramName = ((Param) paramAnno).value(); } } return paramName; } private boolean hasNamedParams(Method method) { boolean hasNamedParams = false; final Object[][] paramAnnos = method.getParameterAnnotations(); for (Object[] paramAnno : paramAnnos) { for (Object aParamAnno : paramAnno) { if (aParamAnno instanceof Param) { hasNamedParams = true; break; } } } return hasNamedParams; } } }

当执行MapperMethod的execute方法的时候,根据当前MapperMethod对应的mapper配置会执行Session的insert, update, delete, select, selectList, selectMap, selectCursor, selectOne或flushStatements方法。
具体执行Session对象的方法对照如下:

Mapper节点 SqlSession方法
insert insert
update update
delete delete
select select: 方法返回void,并且包含resultHandler配置
select selectList:方法返回数组或Collection子类
select selectMap: 存在MapKey注解
select selectCursor: 方法返回Cursor
select selectOne 其它
flush Flush注解

转载于:https://www.cnblogs.com/zsg88/p/7552600.html

MyBatis框架的使用及源码分析(八) MapperMethod相关推荐

  1. 【深入浅出MyBatis系列十一】缓存源码分析

    为什么80%的码农都做不了架构师?>>>    #0 系列目录# 深入浅出MyBatis系列 [深入浅出MyBatis系列一]MyBatis入门 [深入浅出MyBatis系列二]配置 ...

  2. Android-FixBug热修复框架的使用及源码分析(不发版修复bug)

    前面几篇博文已经介绍了2种热修复框架的使用及源码分析,AndFix兼容性比较好,而Dexposed Art处于Beta版. AndFix和Dexposed都是阿里的开源项目.  Alibaba-And ...

  3. 【集合框架】JDK1.8源码分析之HashMap(一)

    转载自  [集合框架]JDK1.8源码分析之HashMap(一) 一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大 ...

  4. mybatis plus 批量保存_mybatis源码分析

    原理图: Configuration解析: Configuration表示配置,该对象中维护了很多mybatis的配置参数: 大致可分为四部分:1.环境变量Environment 2.配置参数:3.缓 ...

  5. Java集合框架之接口Collection源码分析

    本文我们主要学习Java集合框架的根接口Collection,通过本文我们可以进一步了解Collection的属性及提供的方法.在介绍Collection接口之前我们不得不先学习一下Iterable, ...

  6. Spring源码分析八:Mybatis ORM映射框架原理

    文章目录 (一)Mybatis单独操作数据库程序 1.1.数据库表 1.2.建立PO 1.3.建立mapper接口映射 1.4.建立Mybatis配置文件 1.5.建立mapper映射文件 1.6.测 ...

  7. mybatis的使用及源码分析(八) mybatis的rowbounds分析

    Mybatis提供了一个简单的逻辑分页类RowBounds,其原理类似于在内存中做了一个分页,不是数据库层面的分页,性能不算好,谨慎使用 一. RowBounds源码分析 1 RowBounds源码: ...

  8. 【集合框架】JDK1.8源码分析之IdentityHashMap(四)

    一.前言 前面已经分析了HashMap与LinkedHashMap,现在我们来分析不太常用的IdentityHashMap,从它的名字上也可以看出来用于表示唯一的HashMap,仔细分析了其源码,发现 ...

  9. 【集合框架】JDK1.8源码分析HashSet LinkedHashSet(八)

    一.前言 分析完了List的两个主要类之后,我们来分析Set接口下的类,HashSet和LinkedHashSet,其实,在分析完HashMap与LinkedHashMap之后,再来分析HashSet ...

最新文章

  1. c# 扩展方法奇思妙用变态篇四:string 的翻身革命
  2. matlab显示像素分布,MATLAb-----7--------如何动态显示鼠标的坐标值和图像像素值
  3. ListBox类似组件,鼠标右键点击事件得到选中Item
  4. 【流媒体FLV封装协议】
  5. python中的与或非_「Python基础」 While 循环语句
  6. SQLIntegrityConstraintViolationException: 异常解决
  7. Bootstrap3 插件的调用方式
  8. 新技能 get —— 五笔打字
  9. oracle的约束什么作用,Oracle数据库知识之约束
  10. Spring Boot 默认数据源 HikariDataSource 与 JdbcTemplate 初遇
  11. 580刷590bios_老司机带你开车,迪兰570怒刷580BIOS超频至1430M
  12. 没有测量就没有管理,怀念DNW和复习盖洛普Q12
  13. 大数据系列 | 阿里云datav数据可视化(使用json文件生成可视化动态图标)
  14. 安全学习木马查杀打卡第二十一天
  15. 工作日查询(一段日期内的工作日天数查询)
  16. 微信小游戏开发实战教程3-绘制网格
  17. Linux小知识:查看当前最耗费CPU的线程(Arthas工具)
  18. Cesium中自定义材质material
  19. 输入框上的Magnifier
  20. 12月5日计算机考试浙江卷英语答案,英语b级试卷?2019年12月b级真题试卷。

热门文章

  1. 解决python报错FileNotFoundError: [Errno 2] No such file or directory
  2. 周金涛:人生就是一次康波
  3. 数学基础:积分中值定理
  4. Windows系统自带录屏功能,再也不用下录屏软件了
  5. C/C++的灵魂—指针实现冒泡排序、选择排序和插入排序
  6. PaddlePaddle在优化方法处报错 EnforceNotMet: Enforce failed
  7. SpringMVC的数据请求
  8. 佩尔方程以及hdu6222
  9. DOS子程序汇编样例及详解
  10. 【2019多校第一场补题 / HDU6578】2019多校第一场A题1001Blank——dp