侧边栏壁纸
博主头像
拾荒的小海螺博主等级

只有想不到的,没有做不到的

  • 累计撰写 216 篇文章
  • 累计创建 19 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

JAVA:使用 JSQLParser 解析和操作 SQL 的技术指南

拾荒的小海螺
2025-01-08 / 0 评论 / 0 点赞 / 47 阅读 / 7463 字

1、简述

在开发过程中,解析和操作 SQL 是一个常见的需求,例如动态生成 SQL、提取查询中的表名、字段等。JSQLParser 是一个强大的开源 Java 库,用于解析 SQL 并提供语法树操作功能,它支持大部分 SQL 语法并提供清晰的 API,简化了 SQL 操作的复杂性。

样例代码:https://gitee.com/lhdxhl/springboot-example.git

本文将详细介绍如何使用 JSQLParser,并提供常见使用场景的代码示例。

image-pdea.png

2、功能特点

JSQLParser 是一个基于 Java 的 SQL 解析库,可以将 SQL 转换为可操作的语法树。它支持以下功能:

  • 解析 SQL:支持 SELECT、INSERT、UPDATE、DELETE 等语句。
  • 提取信息:获取表名、字段名、条件、函数等。
  • 修改 SQL:动态添加字段、修改条件等。
  • 支持多种 SQL 方言:兼容 MySQL、PostgreSQL、SQL Server 等。

在使用 JSQLParser 之前,需要添加其依赖。以下是 JSQLParser 的 Maven 依赖:

<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>4.6</version>
</dependency>

3、使用样例

  • SQL 监控与审计:提取敏感信息,进行 SQL 安全检查。
  • 动态 SQL 构建:根据业务需求动态拼装 SQL。
  • 多租户支持:在 SQL 中自动注入租户字段。
  • 复杂查询优化:对 SQL 查询进行解析和重写。

3.1 解析并提取 SQL 信息

从一个 SELECT 查询中提取表名、字段和 WHERE 条件。

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;

public class JSQLParserExample {

    public static void main(String[] args) throws JSQLParserException {
        String sql = "SELECT id, name FROM users WHERE age > 18";

        // 解析 SQL
        Select selectStatement = (Select) CCJSqlParserUtil.parse(sql);
        PlainSelect plainSelect = (PlainSelect) selectStatement.getSelectBody();

        // 获取表名
        String tableName = plainSelect.getFromItem().toString();
        System.out.println("表名:" + tableName);

        // 获取字段
        plainSelect.getSelectItems().forEach(item -> System.out.println("字段:" + item));

        // 获取 WHERE 条件
        System.out.println("WHERE 条件:" + plainSelect.getWhere());
    }
}

3.2 动态修改 SQL 查询条件

将 SQL 查询的 WHERE 条件动态添加一个条件。

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;

public class ModifySQLCondition {

    public static void main(String[] args) throws JSQLParserException {
        String sql = "SELECT * FROM orders WHERE status = 'completed'";

        // 解析 SQL
        Select selectStatement = (Select) CCJSqlParserUtil.parse(sql);
        PlainSelect plainSelect = (PlainSelect) selectStatement.getSelectBody();

        // 创建新的条件
        Expression newCondition = CCJSqlParserUtil.parseCondExpression("amount > 100");

        // 修改 WHERE 条件(AND 连接)
        if (plainSelect.getWhere() != null) {
            plainSelect.setWhere(CCJSqlParserUtil.parseCondExpression(
                plainSelect.getWhere() + " AND " + newCondition));
        } else {
            plainSelect.setWhere(newCondition);
        }

        // 输出修改后的 SQL
        System.out.println("修改后的 SQL:" + selectStatement);
    }
}

3.3 获取所有表名

从复杂的 SQL 查询中提取所有涉及的表名(包括 JOIN 表)。

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.Join;

public class ExtractTableNames {

    public static void main(String[] args) throws JSQLParserException {
        String sql = "SELECT a.id, b.name FROM orders a JOIN customers b ON a.customer_id = b.id";

        // 解析 SQL
        Select selectStatement = (Select) CCJSqlParserUtil.parse(sql);
        PlainSelect plainSelect = (PlainSelect) selectStatement.getSelectBody();

        // 主表
        System.out.println("主表:" + plainSelect.getFromItem());

        // JOIN 表
        if (plainSelect.getJoins() != null) {
            for (Join join : plainSelect.getJoins()) {
                System.out.println("JOIN 表:" + join.getRightItem());
            }
        }
    }
}

3.4 添加新的字段

在查询中动态添加一个字段。

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;

public class AddNewField {

    public static void main(String[] args) throws JSQLParserException {
        String sql = "SELECT id, name FROM users";

        // 解析 SQL
        Select selectStatement = (Select) CCJSqlParserUtil.parse(sql);
        PlainSelect plainSelect = (PlainSelect) selectStatement.getSelectBody();

        // 添加新的字段
        SelectExpressionItem newField = new SelectExpressionItem();
        newField.setExpression(CCJSqlParserUtil.parseExpression("email"));
        plainSelect.getSelectItems().add(newField);

        // 输出修改后的 SQL
        System.out.println("修改后的 SQL:" + selectStatement);
    }
}

3.5 解析复杂嵌套查询

解析一个嵌套查询,提取所有表名和字段。

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.Select;

public class ParseNestedSQL {

    public static void main(String[] args) throws JSQLParserException {
        String sql = "SELECT * FROM (SELECT id, name FROM users WHERE age > 18) u WHERE u.name LIKE 'A%'";

        // 解析 SQL
        Select selectStatement = (Select) CCJSqlParserUtil.parse(sql);

        // 输出 SQL 结构
        System.out.println("解析的 SQL:" + selectStatement);
    }
}

4、总结

JSQLParser 是一个功能强大且灵活的 SQL 解析工具。通过它,我们可以轻松解析和操作 SQL,适用于动态查询生成、SQL 安全审计、多租户注入等场景。在实际开发中,根据业务需求灵活使用其 API,可以显著提高开发效率。

推荐实践:

  • 在多租户系统中,使用 JSQLParser 自动注入租户字段。
  • 结合 JSQLParser 解析 SQL 日志,实现精准的 SQL 性能监控。

希望这篇文章能为你带来启发!

0

评论区