Bootstrap

【Mybatis plus】使用分页查询,报错 Parameter ‘xxx‘ not found. Available parameters are xxx


今天工作遇见Mybatis plus 分页查询遇到的错误,提示参数未绑定,现在记录一下。以下数据全部是MOCK信息

0 先给出错误场景

java entity 实体类

用 user 代替实体类信息

@Data
class User {
	private Long id;
	private String name;
	private String agx;
	private Integer age;
}

用 condition 代替查询信息

@Data
class Condition {
	private Long id;
	private String name;
	private String agx;
	private Integer age;
}

java mapper 接口方法

List<User> queryByCondition(Condition condition);
IPage<User> queryByCondition(Page<?> page, Condition condition);

Java mapper 所对应的 mapper.xml 信息

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="please ignore">
    <resultMap id="Base_Column_result_map"
               type="User">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="agx" property="agx"/>
        <result column="age" property="age"/>
    </resultMap>

    <sql id="Base_Column_select_map">
        id, name, agx, age
    </sql>

    <select id="queryByCondition"
            parameterType="Condition"
            resultMap="Base_Column_result_map">
        SELECT
        <include refid="Base_Column_select_map"/>
        FROM
        t_wms_inv_inventory_asset
        <where>
            <if test="id != null">
                AND id = #{id}
            </if>
            <if test="name != null">
                AND name = #{name}
            </if>
            <if test="agx != null">
                AND agx = #{agx}
            </if>
            <if test="age != null">
                AND age = #{age}
            </if>
        </where>
    </select>
</mapper>

异常信息

invoke method = queryByCondition exception
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:78)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440)
	at com.sun.proxy.$Proxy105.selectList(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:223)
	at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.executeForIPage(MybatisMapperMethod.java:122)
	at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:86)
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148)
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
	at com.sun.proxy.$Proxy154.queryInventoryAssets(Unknown Source)
	... 省略部分冗余信息
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]
	at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:212)
	at org.apache.ibatis.scripting.xmltags.DynamicContext$ContextAccessor.getProperty(DynamicContext.java:120)
	at org.apache.ibatis.ognl.OgnlRuntime.getProperty(OgnlRuntime.java:2719)
	at org.apache.ibatis.ognl.ASTProperty.getValueBody(ASTProperty.java:114)
	at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
	at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258)
	at org.apache.ibatis.ognl.ASTNotEq.getValueBody(ASTNotEq.java:50)
	at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
	at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258)
	at org.apache.ibatis.ognl.ASTAnd.getValueBody(ASTAnd.java:61)
	at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
	at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258)
	at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:493)
	at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:457)
	at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:46)
	at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateBoolean(ExpressionEvaluator.java:32)
	at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:34)
	at org.apache.ibatis.scripting.xmltags.MixedSqlNode.lambda$apply$0(MixedSqlNode.java:32)
	at java.util.ArrayList.forEach(ArrayList.java:1257)
	at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:32)
	at org.apache.ibatis.scripting.xmltags.TrimSqlNode.apply(TrimSqlNode.java:55)
	at org.apache.ibatis.scripting.xmltags.MixedSqlNode.lambda$apply$0(MixedSqlNode.java:32)
	at java.util.ArrayList.forEach(ArrayList.java:1257)
	at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:32)
	at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:39)
	at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:297)
	at com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor.intercept(MybatisPlusInterceptor.java:69)
	at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61)
	at com.sun.proxy.$Proxy434.query(Unknown Source)
	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.GeneratedMethodAccessor351.invoke(Unknown Source)
	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:426)
	... 76 common frames omitted

1 解决办法

step1: 给 mapper 接口方法加上具名参数指定,如下:

List<User> queryByCondition(@Param("condition") Condition condition);
IPage<User> queryByCondition(Page<?> page,@Param("condition") Condition condition);

tip: Page 参数不要加 具名 指示

step2: 修改 mapper.xml 的查询信息,都加上 具名 限定

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="please ignore">
    <resultMap id="Base_Column_result_map"
               type="User">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="agx" property="agx"/>
        <result column="age" property="age"/>
    </resultMap>

    <sql id="Base_Column_select_map">
        id, name, agx, age
    </sql>

    <select id="queryByCondition"
            parameterType="Condition"
            resultMap="Base_Column_result_map">
        SELECT
        <include refid="Base_Column_select_map"/>
        FROM
        t_wms_inv_inventory_asset
        <where>
            <if test="condition.id != null">
                AND id = #{condition.id}
            </if>
            <if test="condition.name != null">
                AND name = #{condition.name}
            </if>
            <if test="condition.agx != null">
                AND agx = #{condition.agx}
            </if>
            <if test="condition.age != null">
                AND age = #{condition.age}
            </if>
        </where>
    </select>
</mapper>

到这里已经彻底解决上面的报错问题了,但是出于记录者合格的身份,把为什么报错的原因追踪陈列出来,有兴趣的朋友可以看看。


2 异常原因

mybatis plus 【或者说 mybtais 】会为每个 mapper 接口生成一个 proxy 类对象,用代理类对象的来实现调用 识别并组装 SQL,如下图:
代理类获取
然后调用代理类所代理的接口,去找到合适执行器,从而去解析和分析SQL语句和xml条件语句,如下:SQL解析
执行器的内部的关键代码如图:
获取执行器
如果是用Page + mapper 的形式当作执行方法的参数,那么它最终会定位到这里:
定位执行器
进入executeForPage内部,会发现如下code:
分页执行
其实分页的执行,底层调用的就是 sqlSession 的 selectList 方法【这也从侧面说明了,不能重写 mybatis plus 提供的 BaseMapper 的selectList 方法,因为会在这里出错】。其中 commnad.getName() 方法获取的是接口方法的全限定类名和路径param 是方法的执行参数,也就是pagecondition
最终会有一个拦截器,拦截代理方法的执行,识别和解析参数就在这一步,进入selectList方法,Debug into 进去可以发现,走到了这里:
拦截器
一步步向下点,最终会发现,走到这里,也就是条件节点解析处:
节点解析
node.apply()再调用,还是底部会调用
在这里插入图片描述
next向下,会走到ifSqlNode的判断
在这里插入图片描述
再向下,会走到如下判断,也就是出问题的地方:
在这里插入图片描述
由于没有指定全限定类名,context中的param并不能识别id信息,只能识别poge和condition的信息。

;