阐明
本文涉及的相关软件版别如下:
- mybatis 3.4.x
- HotSpot JDK1.8
- Windows 11
- IDEA 2022.3
一、关于 mapper 的定义和运用
今日一个朋友丢给我如下一段代码: 然后跟我讲为什么本地是好好的, 发布到线上履行就报错。
- BlogMapper.java
public interface BlogMapper {
List<Blog> select(Integer id, String title);
}
- BlogMapper.xml
<select id="select">
select * from my_blog
<where>
<if test="id!=null">
id = #{id}
</if>
<if test="title!=null">
and title = #{title}
</if>
</where>
</select>
- 报错信息
报错信息也很容易了解: mybatis 动态生成 sql
时,提示参数 id 找不到, 只找到了 [arg1, arg0, param1, param2]
这四个可用的参数称号
Caused by: org.apache.ibatis.binding.BindingException:
Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]
二、查看 mybatis 官方文档
mybatis 官网文档阐明如下:
假如你的映射办法承受多个参数,就能够运用这个注解自定义每个参数的姓名。否则在默认情况下,除
RowBounds
以外的参数会以 “param” 加参数方位被命名。例如#{param1}
,#{param2}
。假如运用了@Param("person")
,参数就会被命名为#{person}
。
因而咱们上面的运用方式显着是不对的, 理论上讲这段程序不管在 线上 仍是 本地编辑器 运转, 都是会提示相同的报错的。
三、mybatis 源码剖析
关于 mybatis 是怎样把 mapper.java
的 参数名 绑定到 mapper.xml
中 占位符 上的, 能够直接看 ParamNameResolver.java
这个类
源码中要点内容现已 标识 出来, 咱们只需要关注 要点1 和 要点2
其实源码注释说得很理解了:
- 假如运用了
@Param("称号")
注解, 就用注解中的称号; - 否则, 就调用
isUseActualParamName()
办法; - 假如仍是拿不到, 就由 mybatis 生成。
public static final boolean parameterExists;
static {
boolean available = false;
try {
Resources.classForName("java.lang.reflect.Parameter");
available = true;
} catch (ClassNotFoundException e) {
// ignore
}
parameterExists = available;
}
// java.lang.reflect.Parameter
private String getActualParamName(Method method, int paramIndex) {
if (Jdk.parameterExists) {
return ParamNameUtil.getParamNames(method).get(paramIndex);
}
return null;
}
从上面的源码能够看出, 只需程序能够加载到 java.lang.reflect.Parameter
这个类, 咱们就能拿到参数称号。
/**
* Information about method parameters.
*
* A {@code Parameter} provides information about method parameters,
* including its name and modifiers. It also provides an alternate
* means of obtaining attributes for the parameter.
*
* @since 1.8
*/
public final class Parameter implements AnnotatedElement {
}
从这个类的注释能够看出, 这个类是 JDK1.8 才引进的类, 也就是说咱们是能够拿到真实参数称号的。 那么为什么仍是报错呢?
四、断点调试 mybatis 源码
经过断点调试, 咱们能够看到咱们拿到的参数称号是 arg0
和 arg1
, 并不是咱们希望的 id
和 title
。
这两个称号在咱们前面报错信息的可选值范围内,那么 param0
和 param1
是怎样生成的呢?
param0
和 param1
是 mybatis 为咱们生成的, 用来兜底的, 那么 arg0
和 arg1
是怎样生成的呢 ?
五、Java 编译的相关常识
经过《深入了解 Java 虚拟机》 一说, 咱们能够知道, 字段名是放在 class 文件, 而 class 文件是在编译期生成的。编译的指令是 javac
那么咱们能够尝试查看 javac
指令是否为咱们供给了相关参数来帮咱们获取参数称号。
咱们在指令行工具上履行 javac
指令, 控制台会显现它的所有 可选参数, 其间有一个参数的阐明如下:
-parameters 生成元数据以用于办法参数的反射
意思就是 编译程序 时, 假如加上这个参数, 在程序运转过程中,就能够拿到程序中办法中的 参数称号.
那么咱们 IDEA 编译怎样加上这个参数呢?
除此之外, 咱们还能够经过 IDEA 供给的 jclasslib 插件帮咱们翻译 class 文件:
从上图能够看出, 当咱们加上编译参数时, class 文件中多了一个描绘符. 也就是咱们办法参数的元数据信息.
五、求证
根据上面的剖析, 我立马让我朋友查看了他电脑上 IDEA 编译相关配置, 然后他反应说道, 他之前开发过程中遇到了一些问题(详细什么问题忘记了), 然后稀里糊涂就加了上述配置, 后来也一向没删除,最后今日就碰巧遇上了文中所描绘的 “灵异事件”。
六、强化结论
当对问题以及问题产生的原因有了满足的认知后, 我就明确了检索方向, 然后顺藤摸瓜找到了官方资料来证明咱们的观点, 概况能够参阅 Access to Parameter Names at Runtime
这里我只摘录了其间一段话:
The proposed approach is to create an optional new JVM attribute in version 52.0 class files to store information about the parameters of a JVM-level method
大致意思是在 JDK8 版别中, 能够 选择性 在 class
文件存储 办法的参数称号.