原始 SQL 与脚本

郭胜凯2026/08/17

当通用 CRUD 或 Where 不适合某段 SQL 时,可以继续使用原生 MyBatis XML/注解,也可以调用 SmartMapper 的原始 SQL 方法。

参数化查询

List<Map<String, Object>> rows = studentMapper.queryBySql(
    "SELECT ID, NAME, AGE FROM SM_STUDENT WHERE AGE >= ? AND SEX = ?",
    18,
    Sex.FEMALE
);

返回值是 List<Map<String,Object>>。需要稳定领域模型时,优先声明一个返回实体/DTO 的原生 MyBatis Mapper 方法。

参数化更新

int changed = studentMapper.executeSql(
    "UPDATE SM_STUDENT SET AGE = AGE + 1 WHERE AGE < ?",
    25
);

? 会按顺序转成 MyBatis 绑定参数。值参数不要使用字符串拼接:

// 错误:存在 SQL 注入风险
studentMapper.queryBySql("SELECT * FROM SM_STUDENT WHERE NAME = '" + name + "'");

表名、列名和排序方向不能通过 ? 绑定。需要动态标识符时,应先通过白名单映射到已知 SQL 片段。

Map 重载

框架还提供接收 SQL 和 Map<String,Object> 的底层重载。除非已经熟悉 MyBatis 参数路径,业务通常使用 ? 可变参数重载更清晰。

执行 SQL 脚本

String script = "INSERT INTO CONFIG(NAME) VALUES ('a;b');\n"
    + "-- comment;\n"
    + "INSERT INTO CONFIG(NAME) VALUES ('c');";

int affected = mapper.executeSqlScript(script);

3.0.2 的脚本拆分器会识别:

  • 单引号和双引号字符串
  • MySQL 反引号
  • --# 行注释
  • /* ... */ 块注释
  • 字符串外的分号语句边界

事务边界

executeSqlScript 负责拆分并逐条调用 executeSql,不会自行创建 Spring 事务。需要原子性时,应在调用它的 Service 方法上添加 @Transactional,并确认 DDL 在目标数据库中的提交语义。

只执行随应用发布、受版本控制的可信脚本。不要把 HTTP 请求、用户上传内容或未经校验的管理后台输入交给 executeSqlScript

方言与标识符

原始 SQL 不会经过方言改写。表名、列名、函数、分页和 DDL 都必须符合当前数据库语法。切换 MySQL、H2、Oracle 或 DB2 时,应单独测试原始 SQL;通用 Mapper SQL 才会自动走方言。