Sql语句解析工具类

server/2024/10/15 19:02:00/

需求:

项目 web-sql模块,需要根据 sql 解析获取数据库表,然后对(金库)表权限进行校验。
金库表:用户查询该表前需要审批。

一、Druid (推荐)

添加依赖:

<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.8</version>
</dependency>

工具类:

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);}}
}

测试样例

 @Testvoid 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

添加依赖:

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

工具类:

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) 的需要进一步处理!
例如:

@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]
}

http://www.ppmy.cn/server/132323.html

相关文章

python实现了通过摄像头检测手部动作,根据手指数量的不同映射为特定的视频控制操作

import cv2# 导入OpenCV库,用于图像处理 import mediapipe as mp# 导入MediaPipe库,用于手部检测等 from selenium import webdriver# 导入selenium库 from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.by import By from selenium.webdrive…

AXI4协议数据带宽计算

AXI4 协议数据带宽的计算需要考虑总线频率、数据宽度以及传输特性等因素。其计算公式为&#xff1a;最大传输带宽 总线频率 数据宽度 2&#xff08;单位为 bps&#xff09;1。以下是对该公式的详细解释&#xff1a; 总线频率&#xff1a; 总线频率是指 AXI 总线工作的时钟频…

Vue中组件间通信的6种方式

1、props / $emit 父组件通过props向子组件传递数据&#xff0c;子组件通过$emit和父组件通信。 &#xff08;1&#xff09;父组件向子组件传值&#xff08;props的用法&#xff09; props的特点&#xff1a; props只能是父组件向子组件进行传值&#xff0c;props使得父子组…

【PostgreSQL 】运维篇——未来趋势:发展与社区

PostgreSQL作为一个强大的开源关系数据库管理系统&#xff0c;近年来在功能、性能和社区支持方面取得了显著进展。随着技术的不断演进&#xff0c;PostgreSQL也在不断适应新的需求和挑战。 一、未来发展趋势 云原生与容器化支持 随着云计算的普及&#xff0c;PostgreSQL正在向…

全星魅科技|应急卫星电话|北斗短报文终端|北斗三号短报文终端

北斗三号短报文手持终端是一款集北斗RDSS、北斗RNSS、 GPS、GIS和GPRS与安卓操作平台于一体的手持式智能移动终端。 详细了解请登陆&#xff1a;单北斗|短报文|高精度定位|天通电话|DMR|PDT|对讲|RFID|手持机终端-全星魅 单兵综合集成度高&#xff0c;具备北斗卫星导航系统和全…

安卓13usb触摸唤醒系统 android13触摸唤醒

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 android13在待机后,需要能够使用触摸屏去唤醒我们的系统,这就需要我们修改系统的相关配置了。 2.问题分析 对于这个问题,我们需要知道安卓的事件分发,通过事件分发,…

中间件:SpringBoot集成Redis

目录 1.1 Redis简介 1.1.1 基本信息 1.1.2 数据结构 1.1.3 优势 1.2 Redis基本使用 1.2.1 Windows安装 1.2.2 配置 1.2.3 启动 1.2.4 连接 ​​​​​​编辑 1.2.5 基本操作 1.3 Jedis操作Redis 1.3.1 基本操作 1.3.2 连接池 1.4 SpringBoot操作Redis 1.4.1 基本…

C++:从小白到基础(一)

此文只介绍与C语言不同或有扩展的地方&#xff08;即本文的基础是你已经系统的学习了C的基础语法&#xff09;。 文章目录 C的初步介绍面向对象类&#xff08;class&#xff09;构造函数和析构函数继承多态 引用函数重载默认参数异常处理命名空间&#xff08;namespace&#xf…