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;
            }
        }
    }
  • 相关阅读:
    MSDN Magazine搞错了
    Visual Studio 2005中设置调试符号(Debug Symbols)
    BCB 6的问题
    吴裕雄天生自然Spring Boot使用Spring Data JPA实现人与身份证的一对一关系映射
    吴裕雄天生自然Spring BootSpring Data JPA
    吴裕雄天生自然Spring BootSpring Boot对JSP的支持
    吴裕雄天生自然Spring BootSpring Boot的异常统一处理
    吴裕雄天生自然Spring Boot使用Spring Data JPA实现Author与Article的一对多关系映射
    吴裕雄天生自然Spring Boot解决 Error creating bean with name 'entityManagerFactory' defined in class path resource
    吴裕雄天生自然Spring Boot@ExceptionHandler注解和@ControllerAdvice注解
  • 原文地址:https://www.cnblogs.com/lostyears/p/11267189.html
Copyright © 2011-2022 走看看