解决MyBatis-Plus的mybatis-mate 使用 foreach且使用 #{} 时 错误: Parameter ‘__frch_filter_0‘ not found

报错

Mybatis的Mapper.xml中使用遍历List时报错,报错信息如下:

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

Mapper.xml

 <foreach collection="page.searchFilters" item="filter">
       <if test="filter.condition == 'ig' and filter.column == 'businessType'">
            AND EXISTS (
                SELECT a.id
                FROM DTB_ a a
	                WHERE 1 = 1
	                AND dssii.business_type = #{filter.value} )
            </if>
 </foreach>          

Mapper.java

 IPage<Map<String, Object>> getPageList(@Param("page") Page<DtbAVO, Map<String, Object>> page, @Param("searchData") DtbAVO searchData);

原因:

经过排查最终发现在实现IDataScopeProvider接口实现数据权限时重写的sqlRender方法有问题:

    @Override
    public void sqlRender(Object[] var1, MappedStatement var2, SqlCommandType var3) throws Exception {
        DataScopeProperty var4 = O0000O0o.O0000oOO(var2.getId());
        if (null != var4) {
            ValidateUtils.checkArgument(StringUtils.isNotEmpty(var4.getType()), "[%s]切点的@DataScope注解中的permission属性不能为空!", var2.getId());
            if (var3 == SqlCommandType.INSERT) {
                this.processInsert(var1, var2, var4);
            } else if (var3 == SqlCommandType.UPDATE) {
                this.processUpdate(var1, var2, var4);
            } else if (var3 == SqlCommandType.DELETE) {
                this.processDelete(var1, var2, var4);
            } else if (var3 == SqlCommandType.SELECT) {
                this.processSelect(var1, var2, var4);
            }
        }

    }
    
     public static void processStatements(Object[] var0, MappedStatement var1, DataScopeFunction dataScopeFunction) {
        BoundSql originBoundSql = var1.getBoundSql(var0[1]);
        String originSql = originBoundSql.getSql();
        String processSql = dataScopeFunction.process();
        SearchHandler handler = new SearchHandler(MapUtils.newHashMap(DATA_SCOPE_ARG, processSql));
        TokenDataScopeParser parser1 = new TokenDataScopeParser("@{", "}", handler);
        TokenParserModel parse = parser1.parse(originSql);
        String resultSql1 = parse.getSql();
        var0[0] = O0000oo.O000000o(var1, new BoundSql(var1.getConfiguration(), resultSql1, originBoundSql.getParameterMappings(), originBoundSql.getParameterObject()));
    }

首先我们要知道 标签会创建一个临时的 foreach 集合来处理包含多个元素的集合,而在这个临时的 集合中,每个元素的属性名将被添加一个类似__frch_xxx_yyy的前缀,其中 xxx表示当前元素的属性名 yyy表示索引。在代码中, #{filter.value}使用的是 MyBatis 的预处理语法,会将参数值转换成一个参数占位符,而 MyBatis 会根据参数名来设置参数值,
如何设置参数值很关键,执行的时候需要传入BoundSql对象里面包含已经拼接好的SQL以及映射的参数名parameterMappings及对应参数值additionalParameters,如下图:
解决MyBatis-Plus的mybatis-mate 使用 foreach且使用 #{} 时 错误: Parameter ‘__frch_filter_0‘ not found_第1张图片
但是在sqlRender新建了一个BoundSql,里面缺少了additionalParameters,导致在设置参数值时候找不到值,所以报了上面错误.

解决方法:

1、将 Sql语句中的 #{filter.value} 换为 ${filter.value} ,会将参数值直接替换到 SQL 语句中,因此不会受到参数名不存在的影响,但是要考虑可能会出现 SQL 注入的安全问题。即:

 <foreach collection="page.searchFilters" item="filter">
       <if test="filter.condition == 'ig' and filter.column == 'businessType'">
            AND EXISTS (
                SELECT a.id
                FROM DTB_ a a
	                WHERE 1 = 1
	                AND dssii.business_type = '#{filter.value}' )
            </if>
 </foreach>  

2、重写Rendsql方法的时候,将原来的originBoundSql的additionalParameter赋给新建的boundSql 就好啦!

 BoundSql boundSql = new BoundSql(var1.getConfiguration(), resultSql1, originBoundSql.getParameterMappings(), originBoundSql.getParameterObject());
        for (ParameterMapping mapping : originBoundSql.getParameterMappings()) {
            String prop = mapping.getProperty();
            if (originBoundSql.hasAdditionalParameter(prop)) {
                boundSql.setAdditionalParameter(prop, originBoundSql.getAdditionalParameter(prop));
            }
        }
        var0[0] = O0000oo.O000000o(var1, boundSql);

你可能感兴趣的:(mybatis,java)