首页 / 娱乐 / 明星八卦 / 正文

parsererror(基于springboot+sqlparser从0-1构建 SQL 分析与改写引擎)

放大字体  缩小字体 来源:宝马迷你多少钱 2026-04-15 13:32  浏览次数:11

基于 JSqlParser 搭建一套完整的 SQL 分析与改写引擎,核心是要掌握从SQL 解析AST 操作再到SQL 生成的全流程,并能将其封装成可复用的引擎组件,适配各类业务场景(如合规校验、自动改写、权限控制等)。

基于springboot+sqlparser从0-1构建 SQL 分析与改写引擎nerror="javascript:errorimg.call(this);">

下面我会带你从零构建一个结构化的 SQL 分析与改写引擎,包含核心架构设计、关键功能实现和完整示例代码,你可以直接基于这个框架扩展到自己的业务场景中。


一、引擎整体架构设计

一个通用的 SQL 分析与改写引擎需要具备以下核心模块,各模块职责清晰、可插拔:

    A[SQL输入] --> B[SQL解析模块]    B --> C[AST校验模块]    C --> D{校验结果}    D -->|通过| E[AST改写模块]    D -->|不通过| F[异常提示]    E --> G[SQL生成模块]    G --> H[改写后SQL输出]
  • SQL 解析模块:将原始 SQL 字符串解析为 AST(抽象语法树),处理解析异常;
  • AST 校验模块:遍历 AST 检查 SQL 合规性、风险点(如无 WHERe 的 DELETe、SELECT * 等);
  • AST 改写模块:根据业务规则修改 AST(如追加权限条件、添加 LIMIT、替换表名等);
  • SQL 生成模块:将修改后的 AST 重新生成为合法的 SQL 字符串。

二、环境准备

首先需要引入 JSqlParser 依赖(Maven 为例):

<dependency>    <groupId>net.sf.jsqlparser</groupId>    <artifactId>jsqlparser</artifactId>    <version>4.9</version> <!-- 推荐使用稳定版,最新版可查Maven仓库 --></dependency>

三、核心引擎实现

下面是完整的 SQL 分析与改写引擎代码,封装为可复用的工具类,包含解析、校验、改写三大核心功能:

1. 核心引擎类(SqlRewriteEngine)

import net.sf.jsqlparser.JSQLParserException;import net.sf.jsqlparser.expression.expression;import net.sf.jsqlparser.expression.LongValue;import net.sf.jsqlparser.expression.operators.conditional.Andexpression;import net.sf.jsqlparser.parser.CCJSqlParserUtil;import net.sf.jsqlparser.statement.Statement;import net.sf.jsqlparser.statement.delete.Delete;import net.sf.jsqlparser.statement.select.Limit;import net.sf.jsqlparser.statement.select.PlainSelect;import net.sf.jsqlparser.statement.select.Select;public class SqlRewriteEngine {    // -------------------------- 核心解析方法 --------------------------        public Statement parseSql(String sql) throws JSQLParserException {        if (sql == null || sql.trim().isEmpty()) {            throw new IllegalArgumentException("SQL字符串不能为空");        }        return CCJSqlParserUtil.parse(sql);    }    // -------------------------- 校验模块 --------------------------        public boolean checkDeleteHasWhere(Statement statement) {        if (statement instanceof Delete) {            Delete delete = (Delete) statement;            return delete.getWhere() != null;        }        // 非DELETE语句默认合规        return true;    }        public boolean checkSelectNoStar(Statement statement) {        if (statement instanceof Select) {            Select select = (Select) statement;            if (select.getSelectBody() instanceof PlainSelect) {                PlainSelect plainSelect = (PlainSelect) select.getSelectBody();                // 检查SELECT子句是否包含"*"                return !plainSelect.getSelectItems().stream()                        .anyMatch(item -> item.toString().equals("*"));            }        }        return true;    }    // -------------------------- 改写模块 --------------------------        public Statement addLimitToSelect(Statement statement, int limitNum) {        if (statement instanceof Select) {            Select select = (Select) statement;            if (select.getSelectBody() instanceof PlainSelect) {                PlainSelect plainSelect = (PlainSelect) select.getSelectBody();                // 仅当无LIMIT时添加                if (plainSelect.getLimit() == null) {                    Limit limit = new Limit();                    limit.setRowCount(new LongValue(limitNum));                    plainSelect.setLimit(limit);                }            }        }        return statement;    }        public Statement addPermissionCondition(Statement statement, String permissionCondition) throws JSQLParserException {        expression conditionExpr = CCJSqlParserUtil.parseCondexpression(permissionCondition);        // 处理SELECT语句        if (statement instanceof Select) {            Select select = (Select) statement;            if (select.getSelectBody() instanceof PlainSelect) {                PlainSelect plainSelect = (PlainSelect) select.getSelectBody();                expression oldWhere = plainSelect.getWhere();                // 拼接AND条件:原有WHERe + AND + 权限条件                plainSelect.setWhere(oldWhere == null ? conditionExpr : new Andexpression(oldWhere, conditionExpr));            }        }        // 处理DELETE语句(同理可扩展UPDATE/INSERT)        else if (statement instanceof Delete) {            Delete delete = (Delete) statement;            expression oldWhere = delete.getWhere();            delete.setWhere(oldWhere == null ? conditionExpr : new Andexpression(oldWhere, conditionExpr));        }        return statement;    }    // -------------------------- 生成模块 --------------------------        public String generateSql(Statement statement) {        return statement.toString();    }    // -------------------------- 一站式方法(简化调用) --------------------------        public String processSql(String originalSql, int limitNum, String permissionCondition) throws JSQLParserException {        // 1. 解析SQL        Statement statement = parseSql(originalSql);        // 2. 校验SQL(校验失败可抛出异常)        if (!checkDeleteHasWhere(statement)) {            throw new IllegalArgumentException("DELETE语句必须包含WHERe条件");        }        if (!checkSelectNoStar(statement)) {            throw new IllegalArgumentException("SELECT语句禁止使用SELECT *");        }        // 3. 改写SQL        statement = addLimitToSelect(statement, limitNum);        if (permissionCondition != null && !permissionCondition.trim().isEmpty()) {            statement = addPermissionCondition(statement, permissionCondition);        }        // 4. 生成最终SQL        return generateSql(statement);    }}

2. 引擎使用示例

public class SqlRewriteEngineDemo {    public static void main(String[] args) {        SqlRewriteEngine engine = new SqlRewriteEngine();        // 示例1:处理SELECT语句(添加LIMIT + 行级权限)        try {            String originalSelectSql = "SELECT id, name FROM user WHERe age > 18";            // 改写:添加LIMIT 1000 + 追加部门条件dept_id = '001'            String processedSql = engine.processSql(originalSelectSql, 1000, "dept_id = '001'");            System.out.println("原始SELECT SQL:" + originalSelectSql);            System.out.println("改写后SELECT SQL:" + processedSql);            // 输出:SELECT id, name FROM user WHERe age > 18 AND dept_id = '001' LIMIT 1000        } catch (Exception e) {            e.printStackTrace();        }        // 示例2:处理DELETe语句(校验WHERe + 追加权限)        try {            String originalDeleteSql = "DELETE FROM user WHERe id = 123";            String processedSql = engine.processSql(originalDeleteSql, 0, "dept_id = '001'");            System.out.println("\n原始DELETE SQL:" + originalDeleteSql);            System.out.println("改写后DELETE SQL:" + processedSql);            // 输出:DELETE FROM user WHERe id = 123 AND dept_id = '001'        } catch (Exception e) {            e.printStackTrace();        }        // 示例3:校验失败场景(DELETe无WHERe)        try {            String riskyDeleteSql = "DELETE FROM user";            engine.processSql(riskyDeleteSql, 0, "");        } catch (IllegalArgumentException e) {            System.out.println("\n校验失败:" + e.getMessage());            // 输出:校验失败:DELETE语句必须包含WHERe条件        } catch (Exception e) {            e.printStackTrace();        }    }}

四、引擎扩展建议

你可以基于这个基础引擎,根据业务需求扩展更多功能:

  1. 分库分表适配:解析 SQL 中的分片字段(如 user_id),改写表名(如 user → user_12);
  2. 数据脱敏改写:解析 SELECT 中的敏感字段(如 phone),替换为脱敏函数(如 CONCAt (LEFt (phone,3),'****',RIGHt (phone,4)));
  3. 读写分离路由:识别 SELECT 语句,改写表名前缀(如 user → user_slave);
  4. 慢 SQL 预警:解析 JOIN/ORDER BY 等子句,识别无索引的字段访问;
  5. 跨库语法兼容:将 Oracle 的 ROWNUM 改写为 MySQL 的 LIMIT,或将 MySQL 的 ConCAT 改写为 Oracle 的 ||。

五、关键注意事项

  1. 异常处理:JSqlParser 解析非法 SQL 会抛出JSQLParserException,需捕获并友好提示;
  2. AST 遍历:复杂 SQL(如子查询、联表)需递归遍历 AST 节点,可使用 JSqlParser 的expressionVisitor/StatementVisitor接口;
  3. 性能优化:高频解析场景可缓存解析结果,或使用批量解析;
  4. 兼容性:不同数据库的 SQL 语法差异(如 MySQL vs PostgreSQL),需在改写时做适配。

总结

基于 JSqlParser 构建 SQL 分析与改写引擎的核心要点:

  1. 核心流程:SQL 字符串 → 解析为 AST → 校验 / 改写 AST → 生成新 SQL;
  2. 核心能力:通过操作 AST 实现精准的 SQL 校验和改写(而非字符串替换);
  3. 扩展思路:基于基础引擎封装业务模块(合规校验、权限控制、分库分表等),保持模块可插拔。

这个引擎框架可直接落地到数据安全、数据库中间件、数据治理等业务场景,你可以根据实际需求增减校验规则和改写逻辑。

打赏
0相关评论
热门搜索排行
精彩图片
友情链接
声明:本站信息均由用户注册后自行发布,本站不承担任何法律责任。如有侵权请告知立立即做删除处理。
违法不良信息举报邮箱:115904045
头条快讯网 版权所有
中国互联网举报中心