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]
<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>
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,如下图:
但是在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);