【Mybatis Plus】JSqlParser解析sql语句

ops/2025/2/8 2:14:20/

【Mybatis Plus】JSqlParser解析sql语句

  • 【一】JSqlParser 是什么
  • 【二】JSqlParser 的安装步骤
  • 【三】使用场景
    • 【1】sql语句解析
    • 【2】SQL 语句转换
    • 【3】SQL 语句生成
    • 【4】SQL 语句验证
  • 【四】在使用 JSqlParser 时,如何处理 SQL 注入攻击?
    • 【1】使用预编译语句(Prepared Statements)
    • 【2】使用 JSqlParser 对 SQL 语句进行验证和规范化
    • 【3】白名单机制
    • 【4】使用参数化查询对象
  • 【五】使用 JSqlParser 解析复杂的 SQL 语句
    • 【1】思路
    • 【2】示例代码
  • 【六】解析嵌套sql的案例
    • 【1】解析 SQL 并遍历嵌套结构
    • 【2】解析逻辑
      • (1)解析子查询
      • (2)处理表达式中的子查询
      • (3)处理 UNION/INTERSECT

【一】JSqlParser 是什么

JSqlParser 是一个用于解析 SQL 语句的 Java 库。它可以将 SQL 语句解析为一个 Java 对象树,允许你以编程的方式对 SQL 语句进行分析、修改和操作。它支持多种 SQL 语句类型,包括但不限于 SELECT、INSERT、UPDATE、DELETE、CREATE、ALTER 等。

例如,对于 SQL 语句 “SELECT column1, column2 FROM table1 WHERE column1 = ‘value’”,JSqlParser 可以将其解析为一个 Java 对象,你可以方便地访问该对象的各个部分,如 SELECT 子句中的列名(column1 和 column2)、表名(table1)以及 WHERE 子句中的条件(column1 = ‘value’)等。

【二】JSqlParser 的安装步骤

使用 Maven 进行安装
(1)在 标签内添加以下依赖:

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

(2)测试案例

java">import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;public class JSqlParserExample {public static void main(String[] args) throws Exception {String sql = "SELECT * FROM users WHERE id = 1";Statement statement = CCJSqlParserUtil.parse(sql);System.out.println(statement);}
}

首先,我们导入了 CCJSqlParserUtil 和 Statement 类,它们是 JSqlParser 的一部分。
在 main 方法中,我们定义了一个 SQL 语句字符串 sql
然后,我们使用 CCJSqlParserUtil.parse(sql) 方法将 SQL 语句解析为一个 Statement 对象。
最后,我们将解析后的 Statement 对象打印出来。

【三】使用场景

sql_40">【1】sql语句解析

你可以使用 JSqlParser 来解析 SQL 语句,以提取其中的关键信息。例如,如果你想知道一个 SELECT 语句选择了哪些列、查询了哪个表、使用了哪些条件等,可以通过 JSqlParser 进行解析。以下是一个简单的示例:

java">import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SelectItem;public class JSqlParserExample {public static void main(String[] args) {String sql = "SELECT column1, column2 FROM table1 WHERE column1 = 'value'";try {Statement statement = CCJSqlParserUtil.parse(sql);if (statement instanceof Select) {Select selectStatement = (Select) statement;SelectBody selectBody = selectStatement.getSelectBody();if (selectBody instanceof net.sf.jsqlparser.statement.select.PlainSelect) {net.sf.jsqlparser.statement.select.PlainSelect plainSelect = (net.sf.jsqlparser.statement.select.PlainSelect) selectBody;List<SelectItem> selectItems = plainSelect.getSelectItems();for (SelectItem item : selectItems) {System.out.println("Selected column: " + item);}System.out.println("Table: " + plainSelect.getTable());System.out.println("Where clause: " + plainSelect.getWhere());}}} catch (JSQLParserException e) {e.printStackTrace();}}
}

(1)首先,我们使用 CCJSqlParserUtil.parse(sql) 将 SQL 语句解析为一个 Statement 对象。
(2)然后,我们将 Statement 对象转换为 Select 类型,因为我们知道这是一个 SELECT 语句。
(3)接着,我们通过 getSelectBody() 获取 SelectBody,并将其转换为 PlainSelect 类型,因为大多数简单的 SELECT 语句是 PlainSelect 类型。
(4)最后,我们可以使用 getSelectItems() 获取选择的列,getTable() 获取表名,getWhere() 获取 WHERE 子句。

【2】SQL 语句转换

你可以修改 SQL 语句的某些部分。例如,你可能想要将一个 SELECT 语句中的某些列替换为其他列,或者修改 WHERE 条件。以下是一个示例:

java">import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SelectItem;public class JSqlParserModifyExample {public static void main(String[] args) {String sql = "SELECT column1, column2 FROM table1 WHERE column1 = 'value'";try {Statement statement = CCJSqlParserUtil.parse(sql);if (statement instanceof Select) {Select selectStatement = (Select) statement;SelectBody selectBody = selectStatement.getSelectBody();if (selectBody instanceof net.sf.jsqlparser.statement.select.PlainSelect) {net.sf.jsqlparser.statement.select.PlainSelect plainSelect = (net.sf.jsqlparser.statement.select.PlainSelect) selectBody;// 修改列名plainSelect.getSelectItems().clear();plainSelect.addSelectItems(CCJSqlParserUtil.parseSelectItem("column3, column4"));// 修改 WHERE 条件plainSelect.setWhere(CCJSqlParserUtil.parseCondExpression("column3 > 10"));}System.out.println("Modified SQL: " + statement);}} catch (JSQLParserException e) {e.printStackTrace();}}
}

(1)首先,我们按照上述的解析步骤将 SQL 语句解析为 PlainSelect 类型。
(2)然后,我们使用 getSelectItems().clear() 清除原有的选择项,并使用 addSelectItems() 添加新的选择项。
(3)最后,我们使用 setWhere() 修改 WHERE 条件。

【3】SQL 语句生成

你可以使用 JSqlParser 来构建新的 SQL 语句。例如,你可以使用其 API 来创建一个 SELECT 语句,而不是手动编写 SQL 字符串。以下是一个简单的示例:

java">import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;public class JSqlParserCreateExample {public static void main(String[] args) {// 创建表对象Table table = new Table("table1");// 创建列对象Column column1 = new Column("column1");Column column2 = new Column("column2");// 创建表达式 column1 = 'value'Expression equalsTo = new EqualsTo(column1, CCJSqlParserUtil.parseExpression("'value'"));// 创建表达式 column2 > 10Expression greaterThan = new GreaterThan(column2, CCJSqlParserUtil.parseExpression("10"));// 创建 AND 表达式 column1 = 'value' AND column2 > 10Expression where = new AndExpression(equalsTo, greaterThan);// 创建 SELECT 语句SelectExpressionItem selectItem1 = new SelectExpressionItem(column1);SelectExpressionItem selectItem2 = new SelectExpressionItem(column2);PlainSelect plainSelect = new PlainSelect();plainSelect.setSelectItems(List.of(selectItem1, selectItem2));plainSelect.setTable(table);plainSelect.setWhere(where);Select select = new Select();select.setSelectBody(plainSelect);System.out.println("Generated SQL: " + select);}
}

(1)首先,我们创建表对象和列对象。
(2)然后,我们创建各种表达式,如 EqualsTo 表示等于条件,GreaterThan 表示大于条件,并使用 AndExpression 将它们组合成 WHERE 条件。
(3)接着,我们创建 SelectExpressionItem 作为选择项。
(4)最后,我们将这些元素组合成 PlainSelect 对象,再将其作为 Select 语句的 SelectBody。

【4】SQL 语句验证

你可以使用 JSqlParser 来验证 SQL 语句的语法和结构。例如,在一个 SQL 编辑工具中,你可以使用 JSqlParser 来检查用户输入的 SQL 是否合法。以下是一个简单的示例:

java">import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;public class JSqlParserValidationExample {public static void main(String[] args) {String sql = "SELECT column1, column2 FROM table1 WHERE column1 = 'value'";try {CCJSqlParserUtil.parse(sql);System.out.println("SQL is valid");} catch (JSQLParserException e) {System.out.println("SQL is invalid: " + e.getMessage());}}
}

我们使用 CCJSqlParserUtil.parse(sql) 尝试解析 SQL 语句,如果解析成功,说明 SQL 语句是合法的,否则会抛出 JSQLParserException,表明 SQL 语句存在问题。

【四】在使用 JSqlParser 时,如何处理 SQL 注入攻击?

以下是在使用 JSqlParser 时处理 SQL 注入攻击的一些方法:

【1】使用预编译语句(Prepared Statements)

在 Java 中,使用 JDBC 的预编译语句是防止 SQL 注入的重要手段,JSqlParser 可以与预编译语句结合使用。以下是一个简单的示例:

java">import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class JSqlParserWithPreparedStatement {public static void main(String[] args) {String url = "jdbc:mysql://localhost:3306/your_database";String user = "username";String password = "password";try (Connection connection = DriverManager.getConnection(url, user, password)) {// 假设解析后的 SQL 语句是一个 SELECT 语句String parsedSql = "SELECT * FROM users WHERE username =?";try (PreparedStatement preparedStatement = connection.prepareStatement(parsedSql)) {// 设置参数,这里假设用户输入来自于用户界面或其他来源String userInput = "admin"; preparedStatement.setString(1, userInput);try (ResultSet resultSet = preparedStatement.executeQuery()) {while (resultSet.next()) {// 处理结果集System.out.println(resultSet.getString("username"));}}}} catch (SQLException e) {e.printStackTrace();}}
}

(1)首先,我们使用 DriverManager.getConnection() 建立数据库连接。
(2)然后,我们定义一个包含占位符 ? 的 SQL 语句,这里的 ? 是预编译语句的占位符。
(3)使用 connection.prepareStatement() 创建预编译语句对象。
(4)通过 preparedStatement.setString() 等方法设置参数,这里的参数会被正确转义,避免了 SQL 注入的风险。

【2】使用 JSqlParser 对 SQL 语句进行验证和规范化

JSqlParser 可以用来检查 SQL 语句是否符合预期,例如,可以检查 SQL 语句是否只包含允许的关键字和结构。以下是一个简单的示例:

java">import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;public class JSqlParserValidation {public static void main(String[] args) {String sql = "SELECT * FROM users WHERE username = 'admin' AND 1=1; DROP TABLE users;";try {Statement statement = CCJSqlParserUtil.parse(sql);// 这里可以添加更多的验证逻辑// 例如,检查是否包含不允许的关键字,如 DROP、TRUNCATE 等System.out.println("Parsed SQL: " + statement);} catch (JSQLParserException e) {e.printStackTrace();}}
}

我们使用 CCJSqlParserUtil.parse() 对 SQL 语句进行解析。
在解析后,可以添加额外的验证逻辑,例如检查 SQL 语句中是否包含 DROP、TRUNCATE 等危险的关键字,以防止恶意用户删除或修改数据库结构。

【3】白名单机制

使用白名单来限制 SQL 语句中的表名、列名和操作。以下是一个简单的示例:

java">import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.Select;public class JSqlParserWhiteList {public static final String[] ALLOWED_TABLES = {"users", "products"};public static void main(String[] args) {String sql = "SELECT * FROM users WHERE username = 'admin'";try {Statement statement = CCJSqlParserUtil.parse(sql);if (statement instanceof Select) {Select select = (Select) statement;// 假设我们只允许查询 users 或 products 表String tableName = select.getSelectBody().toString().split("FROM")[1].trim().split(" ")[0];if (!isAllowedTable(tableName)) {throw new RuntimeException("Table not allowed");}System.out.println("Parsed SQL: " + statement);}} catch (JSQLParserException e) {e.printStackTrace();}}private static boolean isAllowedTable(String tableName) {for (String allowedTable : ALLOWED_TABLES) {if (allowedTable.equalsIgnoreCase(tableName)) {return true;}}return false;}
}

我们定义了一个允许的表名数组 ALLOWED_TABLES。
解析 SQL 语句后,对于 SELECT 语句,我们提取出表名,并检查它是否在白名单中。

【4】使用参数化查询对象

JSqlParser 可以帮助你将 SQL 语句转换为参数化查询对象,然后可以与预编译语句结合使用。以下是一个简单的示例:

java">import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.Select;public class JSqlParserParameterized {public static void main(String[] args) {String sql = "SELECT * FROM users WHERE username = 'admin' AND age > 20";try {Statement statement = CCJSqlParserUtil.parse(sql);if (statement instanceof Select) {Select select = (Select) statement;// 假设这里可以提取表达式,如 username = 'admin' 和 age > 20Expression whereExpression = ((Select) statement).getSelectBody().toString().split("WHERE")[1].trim();// 这里可以进一步处理表达式,将其转换为参数化查询对象System.out.println("Parsed Expression: " + whereExpression);}} catch (JSQLParserException e) {e.printStackTrace();}}
}

我们使用 CCJSqlParserUtil.parse() 解析 SQL 语句。
对于 SELECT 语句,我们可以提取 WHERE 子句的表达式,将其作为参数化查询对象,然后与预编译语句结合使用,进一步避免 SQL 注入风险。

【五】使用 JSqlParser 解析复杂的 SQL 语句

【1】思路

(1)导入 JSqlParser 的相关类。
(2)创建一个 SQL 语句的字符串。
(3)使用 CCJSqlParserUtil.parse() 方法将 SQL 语句解析为 Statement 对象。
(4)根据 SQL 语句的不同类型(例如 Select、Insert、Update、Delete),将 Statement 对象进行类型转换。
(5)对转换后的对象进行进一步的操作,提取所需的信息。

【2】示例代码

java">import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SelectItem;import java.util.List;public class JSqlParserComplexExample {public static void main(String[] args) {String complexSql = "SELECT column1, column2, SUM(column3) AS total FROM table1 WHERE column1 > 10 GROUP BY column1, column2 HAVING SUM(column3) > 100 ORDER BY column1 ASC, column2 DESC";try {// 将 SQL 语句解析为 Statement 对象Statement statement = CCJSqlParserUtil.parse(complexSql);// 判断 Statement 对象是否为 Select 语句if (statement instanceof Select) {Select selectStatement = (Select) statement;SelectBody selectBody = selectStatement.getSelectBody();// 提取 Select 语句中的 SelectItemsif (selectBody instanceof net.sf.jsqlparser.statement.select.PlainSelect) {net.sf.jsqlparser.statement.select.PlainSelect plainSelect = (net.sf.jsqlparser.statement.select.PlainSelect) selectBody;List<SelectItem> selectItems = plainSelect.getSelectItems();for (SelectItem item : selectItems) {System.out.println("Select Item: " + item);}// 提取 Where 条件if (plainSelect.getWhere()!= null) {System.out.println("Where Clause: " + plainSelect.getWhere());}// 提取 Group By 子句if (plainSelect.getGroupBy()!= null) {System.out.println("Group By Clause: " + plainSelect.getGroupBy());}// 提取 Having 子句if (plainSelect.getHaving()!= null) {System.out.println("Having Clause: " + plainSelect.getHaving());}// 提取 Order By 子句if (plainSelect.getOrderByElements()!= null) {System.out.println("Order By Clause: " + plainSelect.getOrderByElements());}}}} catch (JSQLParserException e) {e.printStackTrace();}}
}

(1)首先,我们导入了 JSqlParser 所需的类,包括异常处理类 JSQLParserException,解析工具类 CCJSqlParserUtil,以及用于表示 SQL 语句的各种类,如 Statement、Select、SelectBody 和 SelectItem 等。
(2)在 main 方法中,我们定义了一个复杂的 SQL 语句字符串 complexSql。
(3)然后,我们使用 CCJSqlParserUtil.parse(complexSql) 方法将这个复杂的 SQL 语句解析为一个 Statement 对象。
(4)接下来,我们检查这个 Statement 对象是否是 Select 语句(因为我们的示例是一个 SELECT 语句),如果是,我们将其转换为 Select 类型。
(5)对于 Select 语句,我们进一步提取 SelectBody,并判断它是否是 PlainSelect 类型,因为大多数简单的 SELECT 语句会使用 PlainSelect 结构。
(6)我们可以使用 getSelectItems() 方法获取 SELECT 子句中的所有选择项,并遍历打印它们。
(7)对于 WHERE 子句,我们可以使用 getWhere() 方法获取条件表达式,如果存在的话。
(8)对于 GROUP BY 子句,我们可以使用 getGroupBy() 方法获取分组信息,如果存在的话。
(9)对于 HAVING 子句,我们可以使用 getHaving() 方法获取过滤条件,如果存在的话。
(10)对于 ORDER BY 子句,我们可以使用 getOrderByElements() 方法获取排序信息,如果存在的话。

如果你要解析的 SQL 语句是 INSERT、UPDATE 或 DELETE 类型,你可以类似地将 Statement 对象转换为相应的类型,然后使用相应类型的方法提取所需的信息。例如:

java">import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.update.Update;public class JSqlParserOtherExamples {public static void main(String[] args) {String insertSql = "INSERT INTO table1 (column1, column2) VALUES (1, 'value')";String updateSql = "UPDATE table1 SET column1 = 2 WHERE column2 = 'value'";String deleteSql = "DELETE FROM table1 WHERE column1 = 3";try {// 解析 INSERT 语句Statement insertStatement = CCJSqlParserUtil.parse(insertSql);if (insertStatement instanceof Insert) {Insert insert = (Insert) insertStatement;System.out.println("Insert Table: " + insert.getTable());System.out.println("Insert Columns: " + insert.getColumns());System.out.println("Insert Values: " + insert.getItemsList());}// 解析 UPDATE 语句Statement updateStatement = CCJSqlParserUtil.parse(updateSql);if (updateStatement instanceof Update) {Update update = (Update) updateStatement;System.out.println("Update Table: " + update.getTable());System.out.println("Update Set Items: " + update.getSets());System.out.println("Update Where Clause: " + update.getWhere());}// 解析 DELETE 语句Statement deleteStatement = CCJSqlParserUtil.parse(deleteSql);if (deleteStatement instanceof Delete) {Delete delete = (Delete) deleteStatement;System.out.println("Delete Table: " + delete.getTable());System.out.println("Delete Where Clause: " + delete.getWhere());}} catch (JSQLParserException e) {e.printStackTrace();}}
}

(1)对于 INSERT 语句,我们将 Statement 转换为 Insert 类型,然后可以使用 getTable() 方法获取插入的表名,getColumns() 方法获取插入的列名列表,getItemsList() 方法获取插入的值列表。
(2)对于 UPDATE 语句,我们将 Statement 转换为 Update 类型,然后可以使用 getTable() 方法获取更新的表名,getSets() 方法获取更新的列和值的映射,getWhere() 方法获取更新的条件。
(3)对于 DELETE 语句,我们将 Statement 转换为 Delete 类型,然后可以使用 getTable() 方法获取删除的表名,getWhere() 方法获取删除的条件。

sql_516">【六】解析嵌套sql的案例

【1】解析 SQL 并遍历嵌套结构

解析嵌套的 SQL 语句(如包含子查询、多层 JOIN 或 WITH 子句)通常需要借助 SQL 解析器工具,将 SQL 转换为结构化数据(如抽象语法树,AST),然后递归遍历其节点。以下是具体步骤和示例代码:

java">import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.*;public class NestedSqlParser {public static void main(String[] args) throws JSQLParserException {String sql = "SELECT u.name, (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS order_count "+ "FROM users u "+ "WHERE u.id IN (SELECT user_id FROM active_users WHERE status = 'ACTIVE')";Statement statement = CCJSqlParserUtil.parse(sql);if (statement instanceof Select) {Select select = (Select) statement;SelectBody selectBody = select.getSelectBody();processSelectBody(selectBody, 0); // 从嵌套层级 0 开始}}/*** 递归处理 SELECT 语句的嵌套结构* @param selectBody 当前层级的 SELECT 主体* @param level      嵌套层级(用于缩进输出)*/private static void processSelectBody(SelectBody selectBody, int level) {if (selectBody instanceof PlainSelect) {PlainSelect plainSelect = (PlainSelect) selectBody;// 输出当前层级的 SELECTSystem.out.println(indent(level) + "SELECT层级: " + level);// 处理子查询(嵌套 SELECT)for (SelectItem item : plainSelect.getSelectItems()) {if (item instanceof SelectExpressionItem) {SelectExpressionItem exprItem = (SelectExpressionItem) item;if (exprItem.getExpression() instanceof SubSelect) {System.out.println(indent(level) + "发现子查询:");SubSelect subSelect = (SubSelect) exprItem.getExpression();processSelectBody(subSelect.getSelectBody(), level + 1); // 递归处理子查询}}}// 处理 WHERE 子句中的子查询if (plainSelect.getWhere() != null) {plainSelect.getWhere().accept(new ExpressionVisitorAdapter() {@Overridepublic void visit(SubSelect subSelect) {System.out.println(indent(level) + "WHERE子句中的子查询:");processSelectBody(subSelect.getSelectBody(), level + 1);}});}} else if (selectBody instanceof SetOperationList) {// 处理 UNION/INTERSECT 等集合操作SetOperationList setOpList = (SetOperationList) selectBody;for (SelectBody body : setOpList.getSelects()) {processSelectBody(body, level + 1);}}}/** 生成缩进字符串 */private static String indent(int level) {return "  ".repeat(level);}
}

运行上述代码,输出如下:

java">SELECT层级: 0发现子查询:SELECT层级: 1
WHERE子句中的子查询:SELECT层级: 1

【2】解析逻辑

(1)解析子查询

(1)子查询位置
SELECT 列表中的列(如 (SELECT …) AS order_count)。
WHERE 条件中的 IN、EXISTS 等操作符。
FROM 子句中的派生表(如 FROM (SELECT …) AS sub)。

(2)处理方法:通过递归遍历 SelectBody,逐层解析嵌套结构。

(2)处理表达式中的子查询

使用 ExpressionVisitorAdapter 访问 WHERE 条件中的子查询:

java">plainSelect.getWhere().accept(new ExpressionVisitorAdapter() {@Overridepublic void visit(SubSelect subSelect) {// 处理子查询}
});

(3)处理 UNION/INTERSECT

对于包含 UNION 的复杂查询,需处理 SetOperationList:

java">if (selectBody instanceof SetOperationList) {SetOperationList setOpList = (SetOperationList) selectBody;for (SelectBody body : setOpList.getSelects()) {processSelectBody(body, level + 1);}
}

http://www.ppmy.cn/ops/156606.html

相关文章

Spark SQL读写Hive Table部署

个人博客地址&#xff1a;Spark SQL读写Hive Table部署 | 一张假钞的真实世界 官网参考文档&#xff1a;Hive Tables。 本文使用的组件版本如下&#xff1a; Spark 3.1.2Hive 3.1.2 第一步&#xff1a;Hive部署 在所有Worker节点上部署Hive。主要是使用Hive下面的libs。 …

docker和k8s实践

Docker 和 Kubernetes 是现代云原生应用开发和运维中的两个重要技术。Docker 提供容器化技术&#xff0c;可以将应用及其依赖打包在容器中&#xff0c;而 Kubernetes 则负责容器的编排、部署、伸缩和管理。以下是 Docker 和 Kubernetes 的实践指南&#xff0c;涵盖基础安装、配…

【华为OD机试】真题E卷-招聘(Java)

一、题目描述 题目描述: 某公司组织一场公开招聘活动,假设由于人数和场地的限制,每人每次面试的时长不等,并已经安排给定,用(S1,E1)、 (S2,E2)、 (Sj,Ej)…(Si < Ei,均为非负整数)表示每场面试的开始和结束时间。 面试采用一对一的方式,即一名面试官同时只能面试一名…

Windows图形界面(GUI)-QT-C/C++ - QT Dock Widget

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 一、概述 二、使用场景 1. 工具栏 2. 侧边栏 3. 调试窗口 三、常见样式 1. 停靠位置 2. 浮动窗口 3. 可关闭 4. 可移动 四、属性设置 1. 设置内容 2. 获取内容 3. 设置标题 …

Debian 安装 Nextcloud 使用 MariaDB 数据库 + Caddy + PHP-FPM

前言 之前通过 docker在ubuntu上安装Nextcloud&#xff0c;但是现在我使用PVE安装Debian虚拟机&#xff0c;不想通过docker安装了。下面开始折腾。 安装过程 步骤 1&#xff1a;更新系统并安装必要的软件 sudo apt update && sudo apt upgrade -y sudo apt install…

MySQL锁类型(详解)

锁的分类图&#xff0c;如下&#xff1a; 锁操作类型划分 读锁 : 也称为共享锁 、英文用S表示。针对同一份数据&#xff0c;多个事务的读操作可以同时进行而不会互相影响&#xff0c;相互不阻塞的。 写锁 : 也称为排他锁 、英文用X表示。当前写操作没有完成前&#xff0c;它会…

RK3568 opencv播放视频

文章目录 一、opencv相关视频播放类1. `cv::VideoCapture` 类主要构造方法:主要方法:2. 视频播放基本流程代码示例:3. 获取和设置视频属性4. 结合 FFmpeg 使用5. OpenCV 视频播放的局限性6. 结合 Qt 实现更高级的视频播放总结二、QT中的代码实现一、opencv相关视频播放类 在…

我问了DeepSeek和ChatGPT关于vue中包含几种watch的问题,它们是这么回答的……

前言&#xff1a;听说最近DeepSeek很火&#xff0c;带着好奇来问了关于Vue的一个问题&#xff0c;看能从什么角度思考&#xff0c;如果回答的不对&#xff0c;能不能尝试纠正&#xff0c;并帮我整理出一篇不错的文章。 第一次回答的原文如下&#xff1a; 在 Vue 中&#xff0c;…