You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MyBatis Velocity中如何通过参数传递动态SQL实现查询

错误原因

你遇到的报错是MyBatis的模板解析逻辑执行顺序导致的:

  • MyBatis对@Select注解内的模板只会做一次性解析,解析完成后再处理参数替换,你传入的sql字段里的@{}、#if等模板标签不会被二次解析,会直接作为SQL内容发送到数据库,触发语法错误。
  • 第一种写法@Select("@{sql}")使用了预编译参数语法,MyBatis会把整个SQL字符串当成字符串参数,自动加上单引号,最终执行的SQL会变成?'你拼接的SQL字符串',完全不符合SQL语法。
  • 第二种写法@Select("$_parameter.sql")虽然会直接拼接字符串,但拼接后的内容里的模板标签没有被解析,数据库无法识别@{name}这类标签,所以报错。

正确实现方案

方案1:使用@SelectProvider实现动态SQL(最推荐)

这是MyBatis官方提供的动态SQL生成方案,完全可以覆盖你需要的自定义SQL逻辑,同时保证安全性和可维护性:

  1. 定义SQL生成类:
public class UserSqlProvider {
    public String generateFindSql(Parameter param) {
        StringBuilder sql = new StringBuilder("select * from users where 1=1");
        // 替代原来的#if模板逻辑
        if (param.getName() != null) {
            sql.append(" and users.name = #{name}");
        }
        if (param.getId() != null) {
            sql.append(" and users.id = #{id}");
        }
        // 可根据需求自由扩展其他SQL拼接逻辑
        return sql.toString();
    }
}
  1. 调整Mapper写法:
@Mapper
public interface UserMyBatisRepository {
    @SelectProvider(type = UserSqlProvider.class, method = "generateFindSql")
    List<User> find(Parameter p);
}

方案2:传入预处理完成的纯SQL(仅适用于完全可控的场景)

如果你确实需要把SQL完整传入参数,必须先在业务层把所有模板逻辑、变量都替换完成,生成没有任何MyBatis模板标签的可执行SQL,再通过字符串替换语法传入:

  1. Mapper写法:
@Select("${sql}")
List<User> find(Parameter p);
  1. 业务层使用示例:
// 业务层提前处理完所有逻辑,生成纯可执行SQL,不能包含@{}, #if等模板标签
String fullSql = "select * from users where users.name = '张三' and users.id = 1001";
Parameter param = new Parameter("张三", 1001L, fullSql);
List<User> users = userMapper.find(param);

注意:该方案存在极高的SQL注入风险,只有当你完全能控制sql字段的生成来源,没有任何用户输入参与的场景下才能使用。

方案3:自定义LanguageDriver实现二次解析(不推荐)

如果必须要传入带模板标签的SQL片段,可以自定义MyBatis的LanguageDriver,重写SQL解析逻辑,在参数替换完成后对传入的SQL片段做二次模板解析。该方案复杂度高,需要对MyBatis核心逻辑有较深的理解,非必要不使用。

内容的提问来源于stack exchange,提问作者IgorZ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 15:54:04