原始 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 才会自动走方言。
