5.10 mybatis之useActualParamName作用

useActualParamName官方解释:允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8
编译,并且加上 -parameters 选项。(新增于
3.4.1)。useActualParamName有2个值,true和false,如不配置默认为true。

如何理解这句话呢?

假如DAO接口方法中传入的参数名为personId,那么在mapper文件中也想通过#{personId}方式获取参数personId的值,那么需要在mybatis的settings配置中添加useActualParamName=true配置(当然默认为true),并且JDK8以上编译器,并且编译时添加-parameters 选项。

如还不理解,我们继续看下面示例,首先看下useActualParamName=false情况

1. useActualParamName=false

首先在springboot的application.yml文件中添加mybatis的如下配置, 主要是设置use-actual-param-name=false关闭dao方法参数名直接作为mapper文件中SQL的参数名。

mybatis:
  mapper-locations: classpath*:mapper/*Mapper.xml
  configuration:
    cache-enabled: true
    use-actual-param-name: false

1.1 单个参数映射

下面以person表为例,如下所示,表中含有2条数据。
在这里插入图片描述
假设需要查询person_id=14的一条数据,mapper中对应的SQL如下所示,需要获取参数名为personId的值

<resultMap id="resultMap1" type="com.lzj.bean.Person">
    <result column="PERSON_ID" property="id"></result>
    <result column="PERSON_NAME" property="name"></result>
    <result column="PERSON_SEX" property="sex" javaType="com.lzj.bean.SexEnum"></result>
    <result column="PERSON_AGE" property="age"></result>
</resultMap>
<select id="select1" resultMap="resultMap1">
    select * from PERSON where PERSON_ID = #{personId}
</select>

对应的DAO接口如下所示,但是接口中传入的参数名为id,显然与mapper中需要的参数名为personId不一致,下面看一下会怎么样

public interface PersonDao {
    public List<Person> select1(int id);
}

创建测试案例如下所示

@Component
public class PersonService {

    @Autowired
    private PersonDao personDao;

    public void query1(){
        List<Person> persons = personDao.select1(14);
        System.out.println(persons);
    }

启动该测试案例,输出结果如下所示,说明:当只有一个输入参数时,即使DAO接口中参数名与mapper中获取的参数名不一致,DAO 中的参数可以正确到映射到mapper中的参数上,mybatis能正确取到值并执行成功。

2024-04-15 23:09:51.159  INFO 11120 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8004 (http) with context path ''
2024-04-15 23:09:51.177  INFO 11120 --- [           main] com.lzj.MyServiceTest                    : Started MyServiceTest in 8.379 seconds (JVM running for 10.385)
2024-04-15 23:09:51.229  INFO 11120 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-04-15 23:09:52.240  INFO 11120 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-04-15 23:09:52.257 DEBUG 11120 --- [           main] com.lzj.dao.PersonDao.select1            : ==>  Preparing: select * from PERSON where PERSON_ID = ? 
2024-04-15 23:09:52.393 DEBUG 11120 --- [           main] com.lzj.dao.PersonDao.select1            : ==> Parameters: 14(Integer)
2024-04-15 23:09:52.771 DEBUG 11120 --- [           main] com.lzj.dao.PersonDao.select1            : <==      Total: 1
[Person{id=14, name='Tom', sex=SexEnum{description='男性'}, age=25}]

1.2 多个数

前面一个案例是单个输入参数的情况,那么当输入多个参数时会是什么情况呢?
还是以前面库表为例,假设需要查询person_id=14并且person_age=25的数据,对应的mapper 如下所示

<select id="select3" resultMap="resultMap1">
    select * from PERSON where PERSON_ID = #{personId} and PERSON_AGE = #{personAge}
</select>

对应的Dao接口如下所示,我们设置dao方法入参与mapper文件中select3的SQL入参一致,继续观察是否还能正确把值从dao传入到mapper中

public interface PersonDao {
    public List<Person> select3(int personId, int personAge);
}

执行如下测试案例

public void query2(){
    List<Person> persons = personDao.select3(14, 25);
    System.out.println(persons);
}

结果竟然报错了,本次dao接口中参数名与mapper中参数名完全保持一致竟然还没有正确执行,就是因为设置了useActualParamName=false,DAO中传入的参数名分别为personId和personAge,而传递到mybatis后参数名并不再是personId和personAge。

Exception in thread "main" org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'personId' 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.$Proxy49.selectList(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:230)
	at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147)
	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80)
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:57)
	at com.sun.proxy.$Proxy50.select3(Unknown Source)
	at com.lzj.service.PersonService.query2(PersonService.java:22)
	at com.lzj.MyServiceTest.main(MyServiceTest.java:16)
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'personId' not found. Available parameters are [0, 1, param1, param2]
	at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:212)
	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:147)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
	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:483)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)
	... 8 more

下面我们通过断点看一下,dao中参数传递到mapper中变成了啥样,我们看断点的parameter参数,发现mapper要想成功取得参数,需要通过0和1占位符的形式获取参数值,或者通过param1或者param2的方式获取参数值。
在这里插入图片描述
比如我们把mapper修改成占位符为0和1的情况如下所示

<select id="select3" resultMap="resultMap1">
    select * from PERSON where PERSON_ID = #{0} and PERSON_AGE = #{1}
</select>

再执行上面测试案例则成功输出如下所示

…………………………省略启动日志
2024-04-16 00:12:24.055  INFO 8528 --- [           main] com.lzj.MyServiceTest                    : Started MyServiceTest in 5.495 seconds (JVM running for 6.468)
2024-04-16 00:12:24.081  INFO 8528 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-04-16 00:12:24.641  INFO 8528 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-04-16 00:12:24.656 DEBUG 8528 --- [           main] com.lzj.dao.PersonDao.select3            : ==>  Preparing: select * from PERSON where PERSON_ID = ? and PERSON_AGE = ? 
2024-04-16 00:12:24.698 DEBUG 8528 --- [           main] com.lzj.dao.PersonDao.select3            : ==> Parameters: 14(Integer), 25(Integer)
2024-04-16 00:12:24.729 DEBUG 8528 --- [           main] com.lzj.dao.PersonDao.select3            : <==      Total: 1
[Person{id=14, name='Tom', sex=SexEnum{description='男性'}, age=25}]

1.3 param注解

那么对于多个传参情况,mapper中参数名如若与dao接口方法中参数名保持一致,该如何操作呢?大家经常用的@Param注解就登场了。
还是以上面的案例,dao接口方法的参数前面添加@Param注解,如下所示

public interface PersonDao {
    public List<Person> select3(@Param("personId") int personId, @Param("personAge") int personAge);
}

mapper中通过#{personId}和#{personAge}获取参数值

<select id="select3" resultMap="resultMap1">
    select * from PERSON where PERSON_ID = #{personId} and PERSON_AGE = #{personAge}
</select>

重新启动上面测试案例,正确输出结果如下所示

………………省略启动日志
2024-04-16 00:16:51.404  INFO 11772 --- [           main] com.lzj.MyServiceTest                    : Started MyServiceTest in 5.792 seconds (JVM running for 6.766)
2024-04-16 00:16:51.447  INFO 11772 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-04-16 00:16:52.452  INFO 11772 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-04-16 00:16:52.460 DEBUG 11772 --- [           main] com.lzj.dao.PersonDao.select3            : ==>  Preparing: select * from PERSON where PERSON_ID = ? and PERSON_AGE = ? 
2024-04-16 00:16:52.500 DEBUG 11772 --- [           main] com.lzj.dao.PersonDao.select3            : ==> Parameters: 14(Integer), 25(Integer)
2024-04-16 00:16:52.534 DEBUG 11772 --- [           main] com.lzj.dao.PersonDao.select3            : <==      Total: 1
[Person{id=14, name='Tom', sex=SexEnum{description='男性'}, age=25}]

下面我们还是断点一下,DAO接口方法传入的参数传递到mybatis被映射成什么参数了。我们发现Dao中参数名personId和personAge映射到mapper中参数名除了personId和personAge,还有param1和param2,所以在mapper中除了可以用#{personId}和#{personAge}形式,还是可以继续用#{param1}和#{param2}形式,这一点与前面案例相同。
在这里插入图片描述

2. useActualParamName=true

上面一案例已演示可以通过@param注解的方式保持DAO中参数名与mapper中参数名一致,假设有大量的DAO接口以及大量的方法,那么每一个接口中每一个方法的每个参数都要用注解注释一遍,非常繁琐,那么如何简化这一状况呢?

下面就引出了本文的重点,对于mybatis版本大于3.4.1,且java用的JDK8以上,并且编译是添加-parameters参数(比如用idea运行的代码,需要在idea工具中配置;如果用maven编译的,那么需要在编译命令中添加-parameters)。

另外如果使用的spring boot 2.0及以后版本,并且依赖了 spring-boot-starter-parent组件,默认情况启用了-parameter参数,因此无需再刻意配置。dao中参数传入的什么名字,在mapper中直接获取相同的参数名即可。我们通过下面示例验证

首先修改mybatis的配置 如下所示,或者不配置use-actual-parm-name参数,因为默认就为true

mybatis:
  mapper-locations: classpath*:mapper/*Mapper.xml
  configuration:
    cache-enabled: true
    use-actual-param-name: true

修改Dao方法如下所示,删除方法参数前面的@Param注解

public interface PersonDao {
    public int insert(Person person);
    public List<Person> select1(int id);
    public Map select2(int personId);
    public List<Person> select3(int personId, int personAge);
}

mapper中SQL与前面保持一致,直接通过personId和personAge参数名获取传入的值

<select id="select3" resultMap="resultMap1">
    select * from PERSON where PERSON_ID = #{personId} and PERSON_AGE = #{personAge}
</select>

下面spring boot使用2.3.11版本,mybatis使用3.5.2版本,重新启动前面测试案例,输出结果如下所示,正确执行,并输出了库表中数据。

……………………省略
2024-04-16 23:11:35.596  INFO 18012 --- [           main] com.lzj.MyServiceTest                    : Started MyServiceTest in 6.627 seconds (JVM running for 7.951)
2024-04-16 23:11:35.626  INFO 18012 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-04-16 23:11:36.182  INFO 18012 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-04-16 23:11:36.193 DEBUG 18012 --- [           main] com.lzj.dao.PersonDao.select3            : ==>  Preparing: select * from PERSON where PERSON_ID = ? and PERSON_AGE = ? 
2024-04-16 23:11:36.241 DEBUG 18012 --- [           main] com.lzj.dao.PersonDao.select3            : ==> Parameters: 14(Integer), 25(Integer)
2024-04-16 23:11:36.302 DEBUG 18012 --- [           main] com.lzj.dao.PersonDao.select3            : <==      Total: 1
[Person{id=14, name='Tom', sex=SexEnum{description='男性'}, age=25}]

我们继续断点一下,观察下dao中参数传递到mybatis中参数名时什么。发现当useActualParamName=true时,无须使用@Param注解,在mapper中依然可以可以通过dao中参数名personId和personAge获取掺入的参数值。当然也还可以继续通过param1和param2获取参数值,这一点与useActualParamName=false相同。
在这里插入图片描述

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值