MyBatis Mapper 方法参数设置之 ParamNameResolver

调用 Mapper 接口中的如下方法:

List<User> findList(User user);

最终会调用org.apache.ibatis.binding.MapperMethod#executeForMany,其内部会调用org.apache.ibatis.binding.MapperMethod.MethodSignature#convertArgsToSqlCommandParam来获取 Mapper 方法参数:

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

convertArgsToSqlCommandParam 方法会调用org.apache.ibatis.reflection.ParamNameResolver#getNamedParams

public Object convertArgsToSqlCommandParam(Object[] args) {return paramNameResolver.getNamedParams(args);
}

paramNameResolver 属性在创建 MethodSignature 时实例化:

public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {...this.paramNameResolver = new ParamNameResolver(configuration, method);
}

在 ParamNameResolver 的构造方法中会解析方法参数信息,保存到 names 属性中,names 属性类型为SortedMap<Integer, String>

public ParamNameResolver(Configuration config, Method method) {final Class<?>[] paramTypes = method.getParameterTypes();final Annotation[][] paramAnnotations = method.getParameterAnnotations();final SortedMap<Integer, String> map = new TreeMap<Integer, String>();int paramCount = paramAnnotations.length;// get names from @Param annotationsfor (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {if (isSpecialParameter(paramTypes[paramIndex])) {// skip special parameterscontinue;}String name = null;for (Annotation annotation : paramAnnotations[paramIndex]) {if (annotation instanceof Param) {hasParamAnnotation = true;name = ((Param) annotation).value();break;}}if (name == null) {// @Param was not specified.if (config.isUseActualParamName()) {name = getActualParamName(method, paramIndex);}if (name == null) {// use the parameter index as the name ("0", "1", ...)// gcode issue #71name = String.valueOf(map.size());}}map.put(paramIndex, name);}names = Collections.unmodifiableSortedMap(map);
}

方法先获取方法参数的注解(注意是Annotation[][],没有注解的参数会对应一个空数组),然后遍历参数。

循环内部,通过isSpecialParameter方法判断参数是否是特殊参数:

private static boolean isSpecialParameter(Class<?> clazz) {return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
}

可见特殊参数包括:

  • RowBounds
  • ResultHandler

然后遍历参数的注解,如果注解是@Param,则获取注解的 value 属性作为参数名。

如果参数没有被@Param标注(name == null)且开启了 useActualParamName 配置,则通过反射获取方法参数名,如果未获取到(name == null),则使用参数索引(注意是map.size(),而不是方法参数索引)作为参数名。

以参数索引(这里就是方法参数索引了)为键,以得到的参数名为值,保存到 map 集合中,而且注意 map 的类型为TreeMap<Integer, String>,数据会按参数索引从小到大排序。

在循环结束后,将 map 转换为SortedMap<Integer, String>,并赋值给 names 属性。所以最终的 names 如下:

The key is the index and the value is the name of the parameter.
The name is obtained from Param if specified. When Param is not specified, the parameter index is used. Note that this index could be different from the actual index when the method has special parameters (i. e. RowBounds or ResultHandler).
- aMethod(@Param("M") int a, @Param("N") int b) -> {{0, "M"}, {1, "N"}}
- aMethod(int a, int b) -> {{0, "0"}, {1, "1"}}
- aMethod(int a, RowBounds rb, int b) -> {{0, "0"}, {2, "1"}}

也就是:

names 的 key 为参数在形参列表中的索引,value 为参数名。
参数名来自 Param 注解,如果没有被 Param 标注,则使用参数索引作为参数名。注意是参数索引,而不是方法形参列表索引。
比如 aMethod(int a, RowBounds rb, int b) 得到的 names 为 {{0, "0"}, {2, "1"}}

getNamedParams 方法中会用到 names 属性:

public Object getNamedParams(Object[] args) {final int paramCount = names.size();if (args == null || paramCount == 0) {return null;} else if (!hasParamAnnotation && paramCount == 1) {return args[names.firstKey()];} else {final Map<String, Object> param = new ParamMap<Object>();int i = 0;for (Map.Entry<Integer, String> entry : names.entrySet()) {param.put(entry.getValue(), args[entry.getKey()]);// add generic param names (param1, param2, ...)final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);// ensure not to overwrite parameter named with @Paramif (!names.containsValue(genericParamName)) {param.put(genericParamName, args[entry.getKey()]);}i++;}return param;}
}

getNamedParams 方法会根据参数数量和参数注解情况,返回一个参数对象。

  • 如果没有参数,则返回 null。

  • 如果参数数量为 1,且该参数没有被 Param 注解标注,则直接返回参数列表中的唯一参数。

  • 否则返回一个 Map 对象(param),其中 key 为参数名,value 为参数值。参数名从 names 属性中获取,即 names 属性的值。此外,会在 param 中添加默认参数名(param1,param2,...)。

所以有时候会看到,可以在 XML 中使用 param1 之类来做占位符,不使用 Param 注解标注参数,通常也还是能正常工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/883446.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 使用MIDI库演奏《晴天》

那些在MIDI库里徘徊的十六分音符 终究没能拼成告白的主歌我把周杰伦的《晴天》写成C++的类在每个midiEvent里埋藏故事的小黄花调试器的断点比初恋更漫长而青春不过是一串未导出的cmake工程文件在堆栈溢出的夜晚终将明白有些旋律永远停在#pragma once的注释里有些人永远停在未定…

JUC并发—2.Thread源码分析及案例应用

大纲 1.什么是线程以及并发编程 2.微服务注册中心案例 3.以工作线程模式开启微服务的注册和心跳线程 4.微服务注册中心的服务注册功能 5.微服务注册中心的心跳续约功能 6.微服务的存活状态监控线程 7.以daemon模式运行微服务的存活监控线程 8.一般不常用到的ThreadGroup是什么 …

HashMap 的 put 方法源码分析(JDK 1.8)

一、HashMap 的 put 方法源码分析(JDK 1.8) 以下是 HashMap 的 put 方法的源码(JDK 1.8):hash(key) 方法 hash(key) 方法用于计算键的哈希值:如果键为 null,返回 0。否则,返回键的哈希码与高 16 位的异或结果(目的是减少哈希冲突)。putVal 方法 putVal 方法是 HashMa…

第二章笔记

2.1用二进制数表示计算机信息的原因 IC的所有引脚,只有直流电压0V或5V 两个状态。也就是说,IC的一个引脚,只能表示两个状态。 计算机处理信息的最小单位——位,就相当于二进制中的一位。位的英文bit是二进制数位(binary digit)的缩写。 二进制数的位数一般是8位、16位、3…

基金年结、结账、关账和开账

基金年结、结账、关账和开账是基金公司年底财务工作中的重要环节,不仅涉及资产、负债、所有者权益等七大要素的核算,还需要进行全面的账务处理和数据校验。本文将详细解读基金年结的背景、目的、概念以及具体流程,帮助大家深入了解这一复杂而关键的财务操作过程。其实年结不…

分组密码工作模式-CBC

CBC全称密文分组链接工作模式:是分组密码算法的一种工作模式,其特征是将当前的明文分组与前一密文分组进行异或运算后再进行加密得到当前的密文分组。 在CBC模式下,每个明文分组在加密之前,先与反馈至输入端的前一组密文分组按位异或后,再送至加密模块进行加密。其中,IV是…

基于AutoEncode自编码器的端到端无线通信系统matlab误码率仿真

1.算法仿真效果 matlab2022a仿真结果如下(完整代码运行后无水印):仿真操作步骤可参考程序配套的操作视频。2.算法涉及理论知识概要自编码器是一种特殊的神经网络结构,主要由编码器(Encoder)和解码器(Decoder)两部分组成。自编码器的目标是最小化重构误差,常用的重构误…

2-EasyARM i.MX287A开发板 imx-uboot 主线移植

https://github.com/nxp-imx/linux-imx 这里我们使用 lf-6.1.55-2.2.2 分支开始 首先通过硬件原理图找出 ZLG imx287 和 官方开发板 mx28evk 的差异调试串口 mx28evkZLG287这里可以看到官方板用的GPIO3_16/17作为调试串口,ZLG287 GPIO3_16/17被用作I2C,没有接口引出,DUART使…

Langchain的底层原理

Langchain的应用场景 1.个人助手:预定航班 2.学习辅助:参考整个大纲 3.数据分析和数据科学:连接到公司和客户的数据,极大的促进数据的分析

国内服务器docker设置代理【2024年12月】在debian12上测试通过

搬运up主凌冰Koori使用export https_proxy这类环境变量已经失效了。现在可以编辑下面的文件:/etc/docker/daemon.json可能对你来说会提示是新文件,不用管。加入以下配置:{“proxies”: {“http-proxy”: “socks5://127.0.0.1:2080”,“https-proxy”: “socks5://127.0.0.1…

Communication Efficient Large-Scale Training with Adams Convergence Speed

目录概1-bit Adam1-bit SGD代码Seide F., Fu H., Droppo J., Li G. and Yu D. 1-bit stochastic gradient descent and its application to data-parallel distributed training of speed dnns. 2014.Tang H., Gan S., Awan A. A., Rajbhandari S., Li C., Lian X., Liu J., Zh…

基于粒子群算法的网络最优节点部署优化matlab仿真

1.程序功能描述基于粒子群算法的网络最优节点部署优化,实现WSN网络的节点覆盖最大化。 2.测试软件版本以及运行结果展示MATLAB2022A版本运行 3.核心程序%使用PSO优化剩余WSN节点位置以覆盖洞 Numv = 2*(N); func = @(x)fobjs(x,Rmax,area); Vmin = zeros(Numv,1);…