Mybatis @Param源码解析

Mybatis的关键注解@Param源码分析

哈哈,又来吹牛批了,现在要出去找工作很多的都离不开Mybatis框架的使用,并且@Param注解的使用就格外的重要,可以指定sql注入是参数的键就是参数的名称了,但是你可能对Mybatis的内部怎么运行好奇,那让我们来看一看@Param的源码内部是怎么运行的

点击进入@Param注解如下,这就是一个标志而已

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface Param {
    String value();
}

//这就是一个代理对象
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        //这里就是一个三元表达式判断Object与method.getDeclaringClass()的类地址是否相同
        //所以走第二条分支cachedInvoker方法惊醒一定处理
        return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
        //执行invoke(proxy, method, args, this.sqlSession);这个方法是被MapperProxy类重写了,所以调用了invoke(proxy, method, args, this.sqlSession)方法
    } catch (Throwable var5) {
        throw ExceptionUtil.unwrapThrowable(var5);
    }
}
//这个方法就是返回一个类的对象
public Class<?> getDeclaringClass() {
    return clazz;
}

cachedInvoker方法

private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
        try {
            //执行如果不存在,计算的方法,看一下里面在计算什么,this.methodCache是方法的缓存就是使用过的sql语句缓存在这里节约内存使用
            //返回所有value的值,返回都不是默认值,返回到前一个方法
            return (MapperMethodInvoker)MapUtil.computeIfAbsent(this.methodCache, method, (m) -> {
                if (m.isDefault()) {
                    try {
                        return privateLookupInMethod == null ? new DefaultMethodInvoker(this.getMethodHandleJava8(method)) : new DefaultMethodInvoker(this.getMethodHandleJava9(method));
                    } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException var4) {
                        throw new RuntimeException(var4);
                    }
                } else {
                    return new PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));
                }
            });
        } catch (RuntimeException var4) {
            Throwable cause = var4.getCause();
            throw (Throwable)(cause == null ? var4 : cause);
        }
    }



computeIfAbsent 方法

public class MapUtil {
    public static <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<K, V> mappingFunction) {
        V value = map.get(key);
        if (value != null) {
            return value;
        } else {
            mappingFunction.getClass();
            return map.computeIfAbsent(key, mappingFunction::apply);
        }
    }

PlainMethodInvoker是MapperProxy类的静态内部类,是纯方法调用器

private static class PlainMethodInvoker implements MapperMethodInvoker {
    //这里定义了所有的增删查改的所有方法
    private final MapperMethod mapperMethod;

    public PlainMethodInvoker(MapperMethod mapperMethod) {
        this.mapperMethod = mapperMethod;
    }

    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
          //这个方法使用了sql会话并且调用了his.mapperMethod.execute(sqlSession, args)方法
        return this.mapperMethod.execute(sqlSession, args);
    }
}
private final MapperMethod mapperMethod;
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {

}

MapperMethod类如下

//这个类就是对xml映射文件的一些参数的收集

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

public class MapperMethod {
    //getMapper就是创建出来的
    private final SqlCommand command;
    //mapper代理类传过来的
    private final MethodSignature method;

    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new SqlCommand(config, mapperInterface, method);
        this.method = new MethodSignature(config, mapperInterface, method);
    }

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

    private Object rowCountResult(int rowCount) {
        Object result;
        if (this.method.returnsVoid()) {
            result = null;
        } else if (!Integer.class.equals(this.method.getReturnType()) && !Integer.TYPE.equals(this.method.getReturnType())) {
            if (!Long.class.equals(this.method.getReturnType()) && !Long.TYPE.equals(this.method.getReturnType())) {
                if (!Boolean.class.equals(this.method.getReturnType()) && !Boolean.TYPE.equals(this.method.getReturnType())) {
                    throw new BindingException("Mapper method '" + this.command.getName() + "' has an unsupported return type: " + this.method.getReturnType());
                }

                result = rowCount > 0;
            } else {
                result = (long)rowCount;
            }
        } else {
            result = rowCount;
        }

        return result;
    }

    private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
        MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(this.command.getName());
        if (!StatementType.CALLABLE.equals(ms.getStatementType()) && Void.TYPE.equals(((ResultMap)ms.getResultMaps().get(0)).getType())) {
            throw new BindingException("method " + this.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.");
        } else {
            Object param = this.method.convertArgsToSqlCommandParam(args);
            if (this.method.hasRowBounds()) {
                RowBounds rowBounds = this.method.extractRowBounds(args);
                sqlSession.select(this.command.getName(), param, rowBounds, this.method.extractResultHandler(args));
            } else {
                sqlSession.select(this.command.getName(), param, this.method.extractResultHandler(args));
            }

        }
    }

    private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
        Object param = this.method.convertArgsToSqlCommandParam(args);
        List result;
        if (this.method.hasRowBounds()) {
            RowBounds rowBounds = this.method.extractRowBounds(args);
            result = sqlSession.selectList(this.command.getName(), param, rowBounds);
        } else {
            result = sqlSession.selectList(this.command.getName(), param);
        }

        if (!this.method.getReturnType().isAssignableFrom(result.getClass())) {
            return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result);
        } else {
            return result;
        }
    }

    private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {
        Object param = this.method.convertArgsToSqlCommandParam(args);
        Cursor result;
        if (this.method.hasRowBounds()) {
            RowBounds rowBounds = this.method.extractRowBounds(args);
            result = sqlSession.selectCursor(this.command.getName(), param, rowBounds);
        } else {
            result = sqlSession.selectCursor(this.command.getName(), param);
        }

        return result;
    }

    private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
        Object collection = config.getObjectFactory().create(this.method.getReturnType());
        MetaObject metaObject = config.newMetaObject(collection);
        metaObject.addAll(list);
        return collection;
    }

    private <E> Object convertToArray(List<E> list) {
        Class<?> arrayComponentType = this.method.getReturnType().getComponentType();
        Object array = Array.newInstance(arrayComponentType, list.size());
        if (!arrayComponentType.isPrimitive()) {
            return list.toArray((Object[])((Object[])array));
        } else {
            for(int i = 0; i < list.size(); ++i) {
                Array.set(array, i, list.get(i));
            }

            return array;
        }
    }

    private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
        Object param = this.method.convertArgsToSqlCommandParam(args);
        Map result;
        if (this.method.hasRowBounds()) {
            RowBounds rowBounds = this.method.extractRowBounds(args);
            result = sqlSession.selectMap(this.command.getName(), param, this.method.getMapKey(), rowBounds);
        } else {
            result = sqlSession.selectMap(this.command.getName(), param, this.method.getMapKey());
        }

        return result;
    }

    public static class SqlCommand {
        private final String name;
        private final SqlCommandType type;

        public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
            String methodName = method.getName();
            Class<?> declaringClass = method.getDeclaringClass();
            MappedStatement ms = this.resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration);
            if (ms == null) {
                if (method.getAnnotation(Flush.class) == null) {
                    throw new BindingException("Invalid bound statement (not found): " + mapperInterface.getName() + "." + methodName);
                }

                this.name = null;
                this.type = SqlCommandType.FLUSH;
            } else {
                this.name = ms.getId();
                this.type = ms.getSqlCommandType();
                if (this.type == SqlCommandType.UNKNOWN) {
                    throw new BindingException("Unknown execution method for: " + this.name);
                }
            }

        }

        public String getName() {
            return this.name;
        }

        public SqlCommandType getType() {
            return this.type;
        }

        private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration) {
            String statementId = mapperInterface.getName() + "." + methodName;
            if (configuration.hasStatement(statementId)) {
                return configuration.getMappedStatement(statementId);
            } else if (mapperInterface.equals(declaringClass)) {
                return null;
            } else {
                Class[] var6 = mapperInterface.getInterfaces();
                int var7 = var6.length;

                for(int var8 = 0; var8 < var7; ++var8) {
                    Class<?> superInterface = var6[var8];
                    if (declaringClass.isAssignableFrom(superInterface)) {
                        MappedStatement ms = this.resolveMappedStatement(superInterface, methodName, declaringClass, configuration);
                        if (ms != null) {
                            return ms;
                        }
                    }
                }

                return null;
            }
        }
    }

    public static class ParamMap<V> extends HashMap<String, V> {
        private static final long serialVersionUID = -2212268410512043556L;

        public ParamMap() {
        }

        public V get(Object key) {
            if (!super.containsKey(key)) {
                throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + this.keySet());
            } else {
                return super.get(key);
            }
        }
    }
}

这是关于传过来的的参数,对参数进行处理的静态内部类MethodSignature方法

public static class MethodSignature {
        private final boolean returnsMany;
        private final boolean returnsMap;
        private final boolean returnsVoid;
        private final boolean returnsCursor;
        private final boolean returnsOptional;
        private final Class<?> returnType;
        private final String mapKey;
        private final Integer resultHandlerIndex;
        private final Integer rowBoundsIndex;
    	//定义了一个参数解析器的类
        private final ParamNameResolver paramNameResolver;
            public Object convertArgsToSqlCommandParam(Object[] args) {
              //这里调用了参数解析器的一个方法
            return this.paramNameResolver.getNamedParams(args);
        }
    
    
}

这是一个ParamNameResolver类的方法,这里就是参数赋值的地方

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

public class ParamNameResolver {
    //这里定义了一个参数的前缀,param,所以参数会有key是带param的了
    public static final String GENERIC_NAME_PREFIX = "param";
    private final boolean useActualParamName;
    //sql传过来参数的名字
    private final SortedMap<Integer, String> names;
    //这里是判断是否使用了@Param注解注释
    private boolean hasParamAnnotation;

    public ParamNameResolver(Configuration config, Method method) {
        this.useActualParamName = config.isUseActualParamName();
        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) {
                        this.hasParamAnnotation = true;
                        name = ((Param)annotation).value();
                        break;
                    }
                }

                if (name == null) {
                    if (this.useActualParamName) {
                        name = this.getActualParamName(method, paramIndex);
                    }

                    if (name == null) {
                        name = String.valueOf(map.size());
                    }
                }

                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]);
    }
	//Object[] args 注入过来的sql的真实的值
    public Object getNamedParams(Object[] args) {
        //对传过来的参数进行赋值
        int paramCount = this.names.size();
        if (args != null && paramCount != 0) {
            if (!this.hasParamAnnotation && paramCount == 1) {
                //得到sql的值
                Object value = args[(Integer)this.names.firstKey()];
                
                return wrapToMapIfCollection(value, this.useActualParamName ? (String)this.names.get(0) : null);
            } else {
                Map<String, Object> param = new MapperMethod.ParamMap();
                int i = 0;

                for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
                    Map.Entry<Integer, String> entry = (Map.Entry)var5.next();
                    //如果没有使用@Param注解的话,默认传过来的键位 argx
                    //这里就是@Param注解传过来的键和@Param传过来的值
                    param.put(entry.getValue(), args[(Integer)entry.getKey()]);
                    
                    String genericParamName = "param" + (i + 1);
                    if (!this.names.containsValue(genericParamName)) {
                        //这里是paramx键 和param的值 所以有两对
                        param.put(genericParamName, args[(Integer)entry.getKey()]);
                    }
                }

                return param;
            }
        } else {
            return null;
        }
    }

    public static Object wrapToMapIfCollection(Object object, String actualParamName) {
        MapperMethod.ParamMap map;
        if (object instanceof Collection) {
            map = new MapperMethod.ParamMap();
            map.put("collection", object);
            if (object instanceof List) {
                map.put("list", object);
            }

            Optional.ofNullable(actualParamName).ifPresent((name) -> {
                map.put(name, object);
            });
            return map;
        } else if (object != null && object.getClass().isArray()) {
            map = new MapperMethod.ParamMap();
            map.put("array", object);
            Optional.ofNullable(actualParamName).ifPresent((name) -> {
                map.put(name, object);
            });
            return map;
        } else {
            return object;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
MyBatis是一款优秀的持久层框架,它对JDBC的操作数据库的过程进行了封装,使得开发者只需要关注SQL语句本身,而不需要花费精力去处理例如注册驱动、创建连接、创建Statement、手动设置参数、结果集检索等JDBC繁杂的过程代码。在MyBatis,@Param注解用于给SQL语句的参数命名,可以在SQL语句直接引用该参数名,而不是使用默认的参数名。使用@Param注解可以避免因为参数名不同而导致的错误,同时也可以使得SQL语句更加清晰易懂。 举个例子,假设我们有一个查询用户信息的方法: ``` public User getUserById(int id, String name); ``` 如果我们使用MyBatis进行SQL映射,可以这样写: ``` <select id="getUserById" resultType="User"> select * from user where id = #{id} and name = #{name} </select> ``` 其#{id}和#{name}就是使用@Param注解指定的参数名。在Java代码,我们需要这样调用该方法: ``` User user = getUserById(1, "Tom"); ``` 这里的参数名与SQL语句的参数名是一致的,因此MyBatis可以正确地将参数传递给SQL语句执行。如果我们不使用@Param注解,则需要这样写: ``` <select id="getUserById" resultType="User"> select * from user where id = #{arg0} and name = #{arg1} </select> ``` 这里的#{arg0}和#{arg1}是MyBatis默认的参数名,与Java代码的参数名不一致,因此需要手动设置参数: ``` User user = getUserById(1, "Tom"); ``` 使用@Param注解可以避免这种手动设置参数的过程,使得代码更加简洁易懂。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个不爱学习的小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值