zoukankan      html  css  js  c++  java
  • 【Mybatis】Mapper接口的参数处理过程

    下面是一个简单的Mapper接口调用,首先同个session的getMapper方法获取Mapper的代理对象,然后通过代理对象去调用Mapper接口的方法

                EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
                Employee employee = mapper.getEmpByIdAndName(1,"tom");

    Employee getEmpByIdAndName(@Param("id") Integer id, @Param("name") String name, int age);

    源码分析:

    首先看MapperProxy类,关键是mapperMethod.execute(this.sqlSession, args);

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                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 = this.cachedMapperMethod(method);
            return mapperMethod.execute(this.sqlSession, args);
        }

    MapperMethod类:execute方法中,在调用session操作之前,都会调用this.method.convertArgsToSqlCommandParam(args),最终还是调用ParamNameResolver类

        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;
            }
        }

    public Object convertArgsToSqlCommandParam(Object[] args) {
    return this.paramNameResolver.getNamedParams(args);
    }
     
    ParamNameResolver类, 参数会保存成这样的格式{id:1,name:Tom,2:30,param1:1,param2:Tom,param3:30}
    
    
    public class ParamNameResolver {

      private
    static final String GENERIC_NAME_PREFIX = "param"; private final SortedMap<Integer, String> names; private boolean hasParamAnnotation;   // 构造器来初始化names,其值是{0=id, 1=name, 2=2} public ParamNameResolver(Configuration config, Method method) { 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) { if (!isSpecialParameter(paramTypes[paramIndex])) { String name = null; Annotation[] var9 = paramAnnotations[paramIndex]; int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { Annotation annotation = var9[var11]; if (annotation instanceof Param) { // 如果使用了param注解的参数,例如@Param("id") this.hasParamAnnotation = true; name = ((Param)annotation).value(); // name = "id"; break; } } if (name == null) { if (config.isUseActualParamName()) { // 如果配置文件中配置了useActualParamName=true
                   //允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) name
    = this.getActualParamName(method, paramIndex); // name=参数名称(id) } if (name == null) { name = String.valueOf(map.size()); // name=当前map的索引 name=2 } } map.put(paramIndex, name); } } this.names = Collections.unmodifiableSortedMap(map); } private String getActualParamName(Method method, int paramIndex) { return (String)ParamNameUtil.getParamNames(method).get(paramIndex); } private static boolean isSpecialParameter(Class<?> clazz) { return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz); } public String[] getNames() { return (String[])this.names.values().toArray(new String[0]); }      public Object getNamedParams(Object[] args) { // args【1,"Tom",30】
          // names:{0=id, 1=name, 2=2};构造器的时候就确定好了
    int paramCount = this.names.size(); if (args != null && paramCount != 0) { if (!this.hasParamAnnotation && paramCount == 1) { // 如果只有一个元素,并且没有Param注解; return args[(Integer)this.names.firstKey()]; // 直接返回args[0] } else { // 多个元素或者有Param标注 Map<String, Object> param = new ParamMap(); int i = 0; for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) { Entry<Integer, String> entry = (Entry)var5.next();
                /
    /names集合的value作为key; names集合的key又作为取值的参考args[0]

                  //eg:{id=args[0]:1,name=args[1]:Tom,2=args[2]:30}

                        param.put((String)entry.getValue(), args[(Integer)entry.getKey()]);

                //额外的将每一个参数也保存到map中,使用新的key:param1...paramN
                //效果:有Param注解可以#{指定的key},或者#{param1}

                        String genericParamName = "param" + String.valueOf(i + 1);
                        if (!this.names.containsValue(genericParamName)) {
                            param.put(genericParamName, args[(Integer)entry.getKey()]);
                        }
                    }
    
                    return param;
                }
            } else {
                return null;
            }
        }
    }
  • 相关阅读:
    正则表达式的三种模式【贪婪、勉强、侵占】的分析
    php实用的文件上传类
    php简单实用的验证码生成类
    phpstorm不安装apache就可以本地测试PHP
    Ajax技术——带进度条的文件上传
    Mybatis 多表查询及查询结果映射
    关于textarea包在div的自适应问题
    Luogu P3200 [HNOI2009]有趣的数列
    群&置换群&burnside
    卡特兰树
  • 原文地址:https://www.cnblogs.com/lostyears/p/11267189.html
Copyright © 2011-2022 走看看