Sql语句解析工具类

需求:

项目 web-sql模块,需要根据 sql 解析获取数据库表,然后对(金库)表权限进行校验。

金库表:用户查询该表前需要审批。

一、Druid (推荐)

添加依赖:

xml 复制代码
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.2.8</version>
</dependency>

工具类:

java 复制代码
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.*;

import java.util.ArrayList;
import java.util.List;

/**
 * @author NanNan Wang
 */
public class SqlUtil {

    public static List<String> getTableNamesFromSQL(String sql, String dbType) {
        List<String> tableNames = new ArrayList<>();

        // 解析 SQL 语句
        List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);

        for (SQLStatement stmt : stmtList) {
            if (stmt instanceof SQLSelectStatement) {
                SQLSelectStatement selectStatement = (SQLSelectStatement) stmt;
                SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) selectStatement.getSelect().getQuery();

                extractTableNames(queryBlock.getFrom(), tableNames);
            } else if (stmt instanceof SQLInsertStatement) {
                SQLInsertStatement insertStatement = (SQLInsertStatement) stmt;
                tableNames.add(insertStatement.getTableName().getSimpleName());
            } else if (stmt instanceof SQLUpdateStatement) {
                SQLUpdateStatement updateStatement = (SQLUpdateStatement) stmt;
                tableNames.add(updateStatement.getTableName().getSimpleName());
            } else if (stmt instanceof SQLDeleteStatement) {
                SQLDeleteStatement deleteStatement = (SQLDeleteStatement) stmt;
                tableNames.add(deleteStatement.getTableName().getSimpleName());
            }
        }
        return tableNames;
    }

    private static void extractTableNames(SQLTableSource tableSource, List<String> tableNames) {
        if (tableSource instanceof SQLExprTableSource) {
            // 如果是简单表名,直接添加
            SQLExprTableSource exprTableSource = (SQLExprTableSource) tableSource;
            tableNames.add(exprTableSource.getTableName());
        } else if (tableSource instanceof SQLJoinTableSource) {
            // 如果是 JOIN,递归获取左表和右表
            SQLJoinTableSource joinTableSource = (SQLJoinTableSource) tableSource;
            extractTableNames(joinTableSource.getLeft(), tableNames);
            extractTableNames(joinTableSource.getRight(), tableNames);
        } else if (tableSource instanceof SQLSubqueryTableSource) {
            // 如果是子查询,递归处理子查询中的表
            SQLSelect subSelect = ((SQLSubqueryTableSource) tableSource).getSelect();
            extractTableNames(subSelect.getQueryBlock().getFrom(), tableNames);
        }
    }
}

测试样例

java 复制代码
 @Test
 void getTableNamesFromPGSQL() {
     String sql = "SELECT e.id, e.name, d.name AS dept_name FROM schema.employee e " +
             "LEFT JOIN schema.department d ON e.dept_id = d.id WHERE e.salary > 1000";
     List<String> tableNames = SqlUtil.getTableNamesFromSQL(sql, JdbcConstants.POSTGRESQL.name());

     System.out.println("Tables: " + tableNames); //Tables: [employee, department]
 }

二、JSqlParser

添加依赖:

xml 复制代码
<dependency>
  <groupId>com.github.jsqlparser</groupId>
  <artifactId>jsqlparser</artifactId>
  <version>4.6</version>
</dependency>

工具类:

java 复制代码
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.util.TablesNamesFinder;

import java.util.ArrayList;
import java.util.List;

public class SqlUtil {

     /**
     * 使用 JSqlParser 根据 SQL 和数据库类型解析 SQL 中的所有表名
     * @param sql SQL 语句
     * @return 表名列表
     * @throws Exception 解析异常
     */
    public static List<String> extractTableNames(String sql) {
        // 使用 JSqlParser 解析 SQL 语句
        Statement statement = null;
        try {
            statement = CCJSqlParserUtil.parse(sql);
        } catch (JSQLParserException e) {
            throw new RuntimeException(e);
        }

        // 使用 TablesNamesFinder 提取表名
        TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
        return tablesNamesFinder.getTableList(statement);
    }
}

重要:要注意如果是 PostgreSQL 这里支持 模式(schema) 的需要进一步处理!

例如:

java 复制代码
@Test
void extractPgTableNames () {
    String sql = "SELECT e.id, e.name, d.name AS dept_name FROM schema.employee e " +
            "LEFT JOIN schema.department d ON e.dept_id = d.id WHERE e.salary > 1000";
    List<String> tableNames = SqlUtil.extractTableNames(sql);
    System.out.println("Tables: " + tableNames); //Tables: [schema.employee, schema.department]
}
相关推荐
2301_81527952几秒前
Redis如何降低快照对CPU的影响_合理分配RDB执行时机避开业务高峰期
jvm·数据库·python
Greyson18 分钟前
Go语言怎么用GitHub Actions_Go语言GitHub Actions教程【基础】.txt
jvm·数据库·python
qq_342295828 分钟前
CSS如何实现单选按钮自定义样式_利用伪元素隐藏默认UI
jvm·数据库·python
深念Y9 分钟前
状态缓存与TTL:给每个设备状态贴一张“保质期”
数据库·缓存·智能家居·时间·时间戳·智能电视·ttl
m0_6403093010 分钟前
Go语言怎么做链路追踪_Go语言分布式链路追踪教程【精选】.txt
jvm·数据库·python
Jaygee-11 分钟前
WordPress 企业官网搭建教程:用 GMSSH 免费配好 HTTPS、WAF 和网站报表
java·数据库·https
m0_3776182312 分钟前
CSS如何实现背景颜色的棋盘格分布_利用repeating-gradient
jvm·数据库·python
m0_7467523014 分钟前
Less如何简化CSS复杂选择器_使用&连接符提升编写效率
jvm·数据库·python
2301_8135995516 分钟前
HTML函数开发需要SSD吗_SSD对HTML函数开发效率影响【详解】
jvm·数据库·python
qq_3422958216 分钟前
如何在 Pandas 中安全地对非空 DataFrame 执行行级操作
jvm·数据库·python