文章目录
- 1. useActualParamName=false
- 1.1 单个参数映射
- 1.2 多个数
- 1.3 param注解
- 2. useActualParamName=true
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.xmlconfiguration:cache-enabled: trueuse-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 {@Autowiredprivate 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.xmlconfiguration:cache-enabled: trueuse-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相同。