在使用 mybatis 查询的时候, 只需要定义一个查询接口, mybatis 会为我们注入注解实现或是 xml 实现. 但当我们需要传递参数时, 通常需要 @Param 来定义一个名称, 但经常的, 我们也不难发现, 这个名称与参数名称通常是一样的:

User findUser(@Param("username") String username, @Param("password") String password);

如上, username 和 password 都重复了.

而之所以要这么使用, 是因为 xml 中 ${xxx} 所引用的名称就来自于 @Param 里定义的值:

<select id="findUser" resultType="net.xiaogd.demo.mybatis.entity.User">    select * from user where username = #{username} and password = #{password}select>

这就带来一个重复的问题, 可否简化这个定义, 使得无需重复录入名称, 甚至完全地去掉呢? 比如像下面这样:

User findUser(String username, String password);

答案是可以的, 下面就来说下怎么去做到这一点.

前置条件

  1. 首先项目需要使用 jdk8 或以上;

  2. 其次, 需要增加一个编译时的选项 -parameters.

    也即是这样去编译: javac -parameters

通常, 如果没有加上这个选项, 编译后的方法参数签名会变成这样:

User findUser(String arg0, String arg1);

实际的名称会变成如上所示的 arg0arg1 这样没有太多含义的, 毕竟解析器并不关心实际的名称, 有含义的名称只是给人阅读的而已.

下面就说说怎么去引入这个编译选项以使得可以保留有意义的参数名, 包括 maven 中的设置及 IDE 中的设置(包括 Intellij IDEA 和 Eclipse)

maven 编译时的选项

对于 maven, 可以在编译插件 maven-compiler-plugin 中使用 compilerArgs 增加参数来实现:

<plugin>    <groupId>org.apache.maven.pluginsgroupId>    <artifactId>maven-compiler-pluginartifactId>    <configuration>        <compilerArgs>            <arg>-parametersarg>compilerArgs>configuration>plugin>

注: 对于较新的 maven 版本(>= 3.6.2), 也可以直接使用 parameters 配置项:

<plugin>    <groupId>org.apache.maven.pluginsgroupId>    <artifactId>maven-compiler-pluginartifactId>    <configuration>        <parameters>trueparameters>configuration>plugin>

参见: https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#parameters

另注: 如果你使用 spring boot 2.0 及以后的版本并依赖了 spring-boot-starter-parent, 默认情况下已经启用了这一选项:

参见这里的说明: https://stackoverflow.com/questions/31845676/how-to-compile-spring-boot-applications-with-java-8-parameter-flag/49316086#49316086

你可以通过查看最终生效的 Effective POM 来确认这一点, 对于 Eclipse, 在打开的 pom.xml 文件下方选项卡中选择"Effective POM", 然后搜索 maven-compiler-plugin 关键字可以找到相关配置: 

对于 Intellij IDEA, 在 maven 窗口中 右键--Show Effective POM: 

或者通过实际是否正常运行来确认这一点, 如果不是很确定, 当然你可以如上所述在自己的 pom.xml 文件中显式地配置上它.

IDE 编译时保留参数名称

说完了 maven 中的配置, 下面再说说在 IDE 中的类似设置.

注: 通常, 如果 maven 中设置了相应选项, 在项目作为 maven 项目导入并构建时, 这些额外的设置也会生效, 无需额外再作设置. 但考虑到 IDE 的版本及可能存在 bug 等各类原因, 如果在 IDE 中运行不正常, 那么则需要额外检查及配置.

Intellij IDEA 中的设置 -parameters

对于 IDEA, 在下述位置 Settings > Build, Execution, Deployment > Compiler > Java Compiler > Additional command line parameters(额外的命令行参数) 的输入框中, 输入-parameters:

参考: https://stackoverflow.com/questions/39217830/how-to-use-parameters-javac-option-in-intellij

如前所述, 如果没有设置也运行正常, 则不必去设置.

Eclipse 中的设置 -parameters

对于 Eclipse, 则是检查 Store information about method parameters 选项, 看看是否已经是勾选上, 如果没有, 则把它勾上:

如前所述, 如果没有勾选也运行正常或者默认已经勾选上了, 则不必再去勾选.

另注: 因为以上设置涉及编译, 所以通常需要重新编译项目(如果设置后没有自动触发 rebuild), 如果还不生效, 甚至可能需要重启 IDE.

没有配置成功时的异常

如果没有加入 -parameters 选项或因其它原因没有启用成功, 则去掉 @Param 注解后可能会遇到下述异常:

exception: org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]

完整的异常如下:

exception: org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]   at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)    at com.sun.proxy.$Proxy78.selectList(Unknown Source)  at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:230)  at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:137)   at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:75)   at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)  at com.sun.proxy.$Proxy86.findUserByUsernameAndPassword(Unknown Source)   at net.xiaogd.demo.mybatis.dao.user.UserDaoTest.testXmlDao(UserDaoTest.java:76)   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)  at java.lang.reflect.Method.invoke(Method.java:498)   at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)   at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)  at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)   at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)   at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)  at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)  at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)   at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)   at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363)  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)  at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)Caused by: org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2] at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:202) at org.apache.ibatis.reflection.wrapper.MapWrapper.get(MapWrapper.java:45)    at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:122)  at org.apache.ibatis.executor.BaseExecutor.createCacheKey(BaseExecutor.java:219)  at org.apache.ibatis.executor.CachingExecutor.createCacheKey(CachingExecutor.java:146)    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:82)  at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)  at java.lang.reflect.Method.invoke(Method.java:498)   at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)    ... 38 more

那么, 按照其提示, 可以将相应的 xml 语句调整为:

<select id="findUser" resultType="net.xiaogd.demo.mybatis.entity.User">    select * from user where username = #{arg0} and password = #{arg1}select>

或者是使用 param1, param2 这样的名称(注意, 与 arg 从 0 编号不同, 这里是从 1 开始编号)

<select id="findUser" resultType="net.xiaogd.demo.mybatis.entity.User">    select * from user where username = #{param1} and password = #{param2}select>

自然, 使用这些没有太多含义的编号参数名, 代码的可读性就差了很多, 参数是否正确对上了也不容易看出来.

mybatis 版本及 useActualParamName(use-actual-param-name) 的问题

最后, 还有一个配置 useActualParamName(使用实际的参数名称) 可能导致一些异常, 这点与 mybatis 不同版本的缺省配置不同有关, 也与项目本身是否显式配置了这一参数值有关.

在没有启用 -parameters 以保留方法参数名并且没有用 @Param 设置一个有效的名称时, 有时你可能会发现使用 arg0 也还是提示找不到参数:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'arg0' not found. Available parameters are [0, 1, param1, param2]

完整的异常如下:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'arg0' not found. Available parameters are [0, 1, param1, param2]  at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)    at com.sun.proxy.$Proxy78.selectList(Unknown Source)  at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:230)  at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:137)   at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:75)   at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)  at com.sun.proxy.$Proxy86.findUserByUsernameAndPassword(Unknown Source)   at net.xiaogd.demo.mybatis.dao.user.UserDaoTest.testXmlDao(UserDaoTest.java:76)   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)  at java.lang.reflect.Method.invoke(Method.java:498)   at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)   at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)  at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)   at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)   at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)  at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)  at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)   at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)   at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363)  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)  at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)Caused by: org.apache.ibatis.binding.BindingException: Parameter 'arg0' not found. Available parameters are [0, 1, param1, param2]   at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:202) at org.apache.ibatis.reflection.wrapper.MapWrapper.get(MapWrapper.java:45)    at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:122)  at org.apache.ibatis.executor.BaseExecutor.createCacheKey(BaseExecutor.java:219)  at org.apache.ibatis.executor.CachingExecutor.createCacheKey(CachingExecutor.java:146)    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:82)  at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)  at java.lang.reflect.Method.invoke(Method.java:498)   at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)    ... 38 more

而这是由于 mybatis 版本及 useActualParamName(use-actual-param-name) 选项设置的原因.

在早期的版本中(< 3.4.1), useActualParamName 选项的默认值是 false, 而之后的版本(>= 3.4.1), 这个选项的默认值则是 true.

只有设置为 true, 才能利用 -parameters 配置带来的好处.

或者就是项目本身配置了其它不同于缺省的值, 如果使用了 spring boot 项目, 可以检查如下选项:

mybatis.configuration.use-actual-param-name=false

或是在 xml 配置文件中设置:

<setting name="useActualParamName" value="false" />

如果设置了 false, 那么就要写成 #{0}, #{1} 这样:

<select id="findUser" resultType="net.xiaogd.demo.mybatis.entity.User">    select * from user where username = #{0} and password = #{1}select>

如果以前有大量这样的写法, 而你为了兼容它们不想去调整, 那你就无法利用 -parameters 配置带来的好处.

无论是使用 {0}, {1}, 还是使用 {arg0}, {arg1}, 可读性都不是很好, 而且在后续如果需要增加参数, 还容易引入错误, 因此并不推荐这样的写法.

关于启用 -parameters 编译选项简化 mybatis @Param 注解重复问题就介绍到这里.

@param注解_启用 parameters 编译选项简化 mybatis @Param 注解重复问题相关推荐

  1. 怎么实现注解_通透!一口气搞懂注解到底怎么用

    日志脱敏场景简介 在日志里我们的日志一般打印的是 model 的 Json string,比如有以下 model 类 public class Request { /** * 用户姓名 */ priv ...

  2. 自定义JAVA注解_深入理解Java:自定义java注解

    要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解: 元注解的作用就是负责注解其他注解.Java5. ...

  3. java nullable注解_【Java】idea @NotNull @Nullable 注解

    这两个注解在idea里面可以帮助我们检测方法的返回值,方法参数以及局部变量是否为空,从而帮助我们减少一些NPE的发生. 1. 原始注解 @NotNull @Nullable最开始只能使用idea提供的 ...

  4. loginrequired注解_简单实现一个登录验证的注解来保护私有资源

    背景 自定义注解标注受保护的资源访问,当要访问的url被标注了@LoginRequied的时候就变成了一个受保护的资源,需要用户登录或者更进一步需要用户拥有某个权限才能操作.本项目使用的springb ...

  5. spring mysql 注解_【Spring】SpringMVC之基于注解的实现SpringMVC+MySQL

    目录结构: contents structure [-] SpringMVC是什么 MVC的全称是Model View Controller,通过实现MVC框架可以很好的数据.视图.业务逻辑进行分离. ...

  6. 三字经带注解_《三字经》带拼音和注解完美打印版

    rén zhī chū xìng běn shàn xìng xiāng jìn xí xiāng yu ǎ n 人 之 初 性 本 善 性 相 近 习 相 远 [解释]人生下来的时候都是好的,只是由 ...

  7. 注解(7)_元注解_元注解的概念_@Retention_@Target_@Documented_@Inherited

    元注解的概念 元注解是用于修饰其他注解的注解. (1)元注解本身也是注解. (2)元注解是用来修饰别的注解的. JDK5.0提供了四种元注解: @Retention @Target @Document ...

  8. Spring Boot 实战 —— MyBatis(注解版)使用方法

    原文链接: Spring Boot 实战 -- MyBatis(注解版)使用方法 简介 MyBatis 官网 是这么介绍它自己的: MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过 ...

  9. java 注解 属性 类型_跟光磊学Java开发-Java注解

    注解概述 注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记以后,java编译器.开发工具或者其他的框架就可以通过反射来获取类以及类的成员上的注解,然后通过作相应的处 ...

最新文章

  1. flash php socket通信_php与flash as3 socket通信传送文件实现代码
  2. mysql mysqldumpslow_MySQL慢查询日志mysqldumpslow
  3. Vasya and Book
  4. linux 显卡转码,ffmpeg用GPU转码
  5. python好用的第三方库_非常有用的 Python 第三方库
  6. 《编程原本 》一2.2 轨道
  7. 【PHP面向对象(OOP)编程入门教程】20.PHP5接口技术(interface)
  8. android usb 开钱箱_USB打印机开钱箱
  9. matlab里面fig文件坐标轴名称单位,MATLAB如何提取fig文件中的xyz坐标值
  10. 《创新思维训练》2021网课章节测验及答案
  11. python 第一行包含一个整数n、表示行数_输入 第一行输入一个整数n(1 = n = 100)表示测试样例个数 接下来n行,一...
  12. 使用 BEV 投影的高效城市规模点云分割
  13. 闲鱼的统一跨端 API 方案 —— Uni API
  14. 概率抽奖常用两种方式
  15. 优化距离计算函数的matlab实现
  16. 哈工大近世代数期末复习
  17. Nim中文社区官网现已上线!
  18. jzoj3234. 阴阳
  19. 一波回忆杀,这个网站可以让我玩上一整天!
  20. 低代码构建物联网平台,让物联网项目更简单

热门文章

  1. linux 内存清理 释放命令,Linux系统中的内存清理和释放命令总结
  2. python 多进程 multiprocessing 进程池 pool apply_async()函数与apply()函数的用法
  3. Intel Realsense D435 如何获取摄像头的内参?get_profile() video_stream_profile() get_intrinsics()
  4. html判断是否有某个元素,jquery怎么判断元素是否存在?
  5. 安装mysql Install/Remove of the Service Denied!错误的解决办法
  6. 史上最详细Docker安装Redis (含每一步的图解)实战
  7. cisco 2960 VLAN MAC_华为网络初级工程师快速掌握基于MAC地址的VLAN划分实用收藏
  8. python期末考试编程题_Python_编程题期末必看
  9. matlab矩阵对某一列求和,将矩阵中的每一列与另一列中的对应行相乘,然后在Matlab中求和...
  10. 简述python中怎样导入模块_12 python中模块和包如何导入