Mybatis源码解析-接口方法参数绑定(旧)

有时候,不是因为你没有能力,也不是因为你缺少勇气,只是因为你付出的努力还太少,所以,成功便不会走向你。而你所需要做的,就是坚定你的梦想,你的目标,你的未来,然后以不达目的誓不罢休的那股劲,去付出你的努力,成功就会慢慢向你靠近。

导读:本篇文章讲解 Mybatis源码解析-接口方法参数绑定(旧),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

准备工作

public interface UserDao {
    User getUserOne(Integer id, User user, @Param("nameParam") String name, List<String> idList);
}
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.getUserOne(1,new User(1,"zhangsan"),"李四");

代理对象
在这里插入图片描述

源码

1)实现InvocationHandler,动态代理

public class MapperProxy<T> implements InvocationHandler, Serializable {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
        	//objet中toStrong、hashCode类似方法,直接反射即可
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
			//接口中的默认方法,这里暂时不研究
            if (method.isDefault()) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
		//获取MapperMethod
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        //执行sql语句
        return mapperMethod.execute(this.sqlSession, args);
    }
}

1、MapperMethod类内部类SqlCommand command。封装方法全限定名,以及sql标签枚举类型 insert,update,delete,select
在这里插入图片描述
2、MapperMethod类内部类MethodSignature method。封装方法的返回值和参数信息
在这里插入图片描述

2)mapperMethod.execute(this.sqlSession, args) 执行sql语句

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    Object param;
    //根据枚举类型进入增删改查
    switch(this.command.getType()) {
    case INSERT:
        param = this.method.convertArgsToSqlCommandParam(args);
        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()) {
            result = this.executeForMap(sqlSession, args);
        } else if (this.method.returnsCursor()) {
            result = this.executeForCursor(sqlSession, args);
        } else {
            param = this.method.convertArgsToSqlCommandParam(args);
            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;
    }
}

3)param = this.method.convertArgsToSqlCommandParam(args) 获取参数

//内部类
public static class MethodSignature {
    private final ParamNameResolver paramNameResolver;
    ...
    public Object convertArgsToSqlCommandParam(Object[] args) {
    	//方法调用前ParamNameResolver创建对象,看构造函数
        return this.paramNameResolver.getNamedParams(args);
    }
    ...
}   

获取name值,相当于key,后续会用到

public class ParamNameResolver {
    private static final String GENERIC_NAME_PREFIX = "param";
    private final SortedMap<Integer, String> names;
    private boolean hasParamAnnotation;

    public ParamNameResolver(Configuration config, Method method) {
    	//获取参数所有的Class类型
        Class<?>[] paramTypes = method.getParameterTypes();
        //获取每个参数的注解-每个参数可能有多个注解,故二维数组
        Annotation[][] paramAnnotations = method.getParameterAnnotations();
        SortedMap<Integer, String> map = new TreeMap();
        int paramCount = paramAnnotations.length;

        for(int paramIndex = 0; paramIndex < paramCount; ++paramIndex) {
        	// 检测当前的参数类型是否为 RowBounds 或 ResultHandler(是否特殊参数)
            if (!isSpecialParameter(paramTypes[paramIndex])) {
                String name = null;
                Annotation[] var9 = paramAnnotations[paramIndex];
                int var10 = var9.length;
				//多个注解,只获取@param注解中的value值作为name
                for(int var11 = 0; var11 < var10; ++var11) {
                    Annotation annotation = var9[var11];
                    if (annotation instanceof Param) {
                        this.hasParamAnnotation = true;
                        name = ((Param)annotation).value();
                        break;
                    }
                }

                if (name == null) {
                	//useActualParamName 配置项开启,name则为方法上参数名字
                    if (config.isUseActualParamName()) {
                        name = this.getActualParamName(method, paramIndex);
                    }
					//如果没有注解,上面配置项没开启,name则为从0开始的数字字符串
                    if (name == null) {
                        name = String.valueOf(map.size());
                    }
                }

                map.put(paramIndex, name);
            }
        }

        this.names = Collections.unmodifiableSortedMap(map);
    }
}

mybatis配置选项useActualParamName 允许使用方法签名中的名称作为参数名称,但必须采用java8编译,并且加上-parameters选项
不满足以上条件,则会变成arg0、arg1…
在这里插入图片描述
private final SortedMap<Integer, String> names; 两种方式结果
在这里插入图片描述
在这里插入图片描述

4)核心代码

public Object getNamedParams(Object[] args) {
    int paramCount = this.names.size();
    if (args != null && paramCount != 0) {
    	//如果没@Param注解,且参数只有一个
        if (!this.hasParamAnnotation && paramCount == 1) {
            return args[(Integer)this.names.firstKey()];
        } else {
            Map<String, Object> param = new ParamMap();
            int i = 0;
			//遍历上面的map,将names value及arg0或者id作为key,通过names key作为索引获取arg[key]参数
            for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
                Entry<Integer, String> entry = (Entry)var5.next();
                param.put((String)entry.getValue(), args[(Integer)entry.getKey()]);
                //再添加一组 key = param1  value  = 参数组的数组
                String genericParamName = "param" + String.valueOf(i + 1);
                if (!this.names.containsValue(genericParamName)) {
                    param.put(genericParamName, args[(Integer)entry.getKey()]);
                }
            }

            return param;
        }
    } else {
        return null;
    }
}
  • 如果参数没有@Param注解,且参数只有一个,直接通过Object[] args的arg[0]获取参数,此时参数名称无论写什么都可以

在这里插入图片描述

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/148671.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!