AI 生成 SQL 模板以后,为什么还需要固定 helper 规则

上一篇聊到,LLM 很适合帮 Spring 企业项目生成 SQL 模板。

它擅长 SQL,也能比较快理解"列表页有哪些筛选条件""哪些字段要返回""哪些条件为空时跳过"。如果我们让它输出一段完整 SQL 模板,而不是输出 Java 里的 StringBuilder 拼接,人工审查成本会低很多。

但这里还有一个问题:既然已经是 SQL 模板了,为什么还要规定 sqlExpsqlInExptoLikeStr 这些 helper?

原因很简单:AI 能写 SQL,不代表它每次都会用同一种方式处理动态条件。

如果不给规则,它很可能写出这样的模板:

javascript 复制代码
export const sql = `
    SELECT *
    FROM orders o
    WHERE o.deleted = 0
        ${form.param.teamId ? `AND o.team_id = ${form.param.teamId}` : ''}
        ${form.param.keyword ? `AND o.order_no LIKE '%${form.param.keyword}%'` : ''}
`;

这段看起来也像模板,但问题很多。

第一,参数被直接拼进 SQL,注入风险不用多说。第二,字符串、数字、时间、IN 列表要各自处理转义和格式,模型很容易漏掉细节。第三,每个开发者、每次对话、每个模型版本都可能生成不同风格,最后团队维护的不是一套模板,而是一堆写法。

所以 helper 的作用,不是把 SQL 变复杂,而是把容易出错的部分固定下来。

比如单值条件:

javascript 复制代码
${sqlExp(form.param.teamId, 'AND o.team_id = ?')}

这条规则很小,但它同时约定了几件事:

  • 值为空时不输出这段条件;
  • 值不为空时输出 AND o.team_id = ?
  • 参数进入 evaluator 的参数列表;
  • SQL 仍然保持 PreparedStatement 风格。

这样 LLM 不需要自己判断"空值怎么跳过""参数放在哪里""要不要拼引号"。它只需要选对字段和条件。

再看 IN

javascript 复制代码
${sqlInExp(form.param.statusList, 'AND o.status IN ')}

IN 是动态 SQL 里很容易写乱的地方。列表为空怎么办?有 1 个、3 个、20 个值时,占位符怎么展开?参数顺序怎么和占位符对应?

如果每次都让人或 LLM 现场写,就会反复出现这种代码:

java 复制代码
sql.append(" and o.status in (");
sql.append(statusList.stream().map(x -> "?").collect(joining(",")));
sql.append(")");
args.addAll(statusList);

它不难,但很烦,而且容易在复制修改时漏掉边界。sqlInExp 把这个模式收掉以后,模板里只剩业务含义:这里要按状态列表筛选。

LIKE 也是一样。

javascript 复制代码
${sqlExp(toLikeStr(form.param.keyword), 'AND o.order_no LIKE ?')}
${sqlExp(toLikeStrR(form.param.mobile), 'AND c.mobile LIKE ?')}

模糊查询看起来简单,实际很容易不统一。有的地方写 %keyword%,有的地方写 keyword%,有的地方忘了处理空字符串。固定成 toLikeStrtoLikeStrLtoLikeStrR 以后,模型只需要表达"这是两端模糊"还是"这是右侧模糊"。

对人也是一样。

代码评审时,不需要重新判断这段模板有没有手工拼参数,只要看几个点:

  • 可选条件是否都用了 sqlExp
  • 多值条件是否用了 sqlInExp
  • 模糊匹配方向是否符合业务;
  • 必带条件是否用了明确的强制规则;
  • 字段、表、JOIN 和排序是否符合查询目标。

这比审一段自由发挥的动态 SQL 轻很多。

更关键的是,这套规则对 LLM 很友好。

提示词里不用解释一整套框架,只要给几条短规则:

text 复制代码
可选单值条件:sqlExp(value, 'AND field = ?')
多值 IN 条件:sqlInExp(list, 'AND field IN ')
LIKE 条件:先用 toLikeStr / toLikeStrL / toLikeStrR 处理值
不要直接把参数拼进 SQL 字符串

这类规则短、稳定、容易举例。模型在生成第一个模板以后,后续改查询条件、加字段、拆时间范围,基本都会沿着同一种写法继续补。

这就是固定 helper 的价值:减少自由度。

听起来好像"减少自由度"不够酷,但企业开发里很多质量问题,恰恰来自不必要的自由度。一个订单列表页、一个导出接口、一个运营报表,不需要十种动态 SQL 写法。它需要的是一套团队看得懂、AI 也容易遵守的规则。

当然,helper 也不是万能的。

它解决的是动态 SQL 生成过程里的工程问题:空值跳过、参数收集、IN 展开、LIKE 处理、调试可读性。它不能替你判断业务字段口径,也不能替你判断某个用户该不该看到这批数据。

所以这一阶段的边界要说清楚:我们不是在做一个让 AI 自动决定业务含义的系统,也不是让 AI 绕过权限去查库。我们只是先把"复杂动态 SQL 怎么生成、怎么审、怎么调试"这件事变得规整一点。

对一个已经长期使用 Spring JPA / Hibernate 的项目来说,这个收益很直接。

简单查询继续用 Repository。复杂列表、报表、导出这些 SQL 味很重的地方,可以让 LLM 帮忙生成模板初稿,再用固定 helper 把动态部分收束住。人最后审的是一段完整 SQL 和一组稳定规则,而不是一堆 Java 拼接分支。

这一步仍然很小,但它让 AI 辅助开发从"生成一段看起来能跑的代码",变成"生成一段团队能接住的模板"。

相关代码:foggy-dataset Java 引擎模块

github.com/foggy-proje...

相关文档:FSScript SQL helper functions

github.com/foggy-proje...

相关推荐
明天一点1 小时前
Cloudflare 通知转发钉钉机器人
前端·后端
前端Hardy1 小时前
前端日历组件,要变天了?Schedule-X v4.6 彻底杀疯了
前端·javascript·后端
Oo_行者_oO1 小时前
微服务 Feign 从“万能公共服务”到“业务客户端”
后端·架构
wei_shuo1 小时前
别再踩坑了!KingbaseES 存储过程与触发器开发避坑实录
后端
元宝骑士1 小时前
MySQL 实战:跨表排序 + 指定类型置顶四种写法
后端·mysql
ConardLi2 小时前
啊?我刚开源的 Skills 已经 7K Star 了?!
前端·人工智能·后端
道友可好2 小时前
Git Worktree:一个仓库,多个分身
前端·后端·程序员
鱼鳞_2 小时前
苍穹外卖-Day10(Spring task)
java·后端·spring
我是一颗柠檬3 小时前
【Redis】事务与Lua脚本Day7(2026年)
数据库·redis·后端·lua·database