
数据库【免费下载链接】JSqlParserJSqlParser parses an SQL statement and translate it into a hierarchy of Java classes. The generated hierarchy can be navigated using the Visitor Pattern项目地址https://gitcode.com/gh_mirrors/js/JSqlParser点击查看免费下载导读JSqlParser 4.7 对 SQL 抽象语法树AST的访问方式做了一次彻底的重写目标是让开发者更简单地遍历和操作解析结果。本次升级合并或移除了大量冗余类如ValueListExpression、ValuesStatement、SelectBody、SubSelect、SubJoin、ItemsList、MultiExpressionList以及各类SelectItem派生类API 变化较大但迁移路径清晰。本文以官方迁移文档src/site/sphinx/migration47.rst为骨架结合仓库源码逐一讲解每个破坏性变更的替代方案、新旧 API 对照、AST 结构变化与完整可运行的迁移示例帮助你快速把基于旧版 API 的代码平滑升级到 4.7。一、Values子句两大旧类统一收编4.7 之前解析VALUES关键字需要面对两个不同的类作为语句出现的ValuesStatement以及作为表达式出现的ValueListExpression。4.7 中这两者被统一替换为单一的Values类——它同时实现了Select、Statement和Expression三套接口一个类覆盖所有使用场景。从源码可以看到这一设计Values.java 声明为public class Values extends Select implements FromItem内部持有ExpressionListExpression expressions与可选的Alias alias并同时提供了accept(SelectVisitor, context)与accept(FromItemVisitor, context)两个访问入口。也就是说它既能作为独立的 SQL 语句被解析也能作为子查询、表达式出现在其他语句的任意位置。作为独立 Statement 使用VALUES ( 1, 2, 3 ) ;解析出的 AST 结构SQL Text └─Statements: statement.select.Values └─ParenthesedExpressionList: (1, 2, 3)对应的 Java 迁移代码Values values (Values) CCJSqlParserUtil.parse(sqlStr); assertEquals( 3, values.getExpressions().size() );顶层解析结果直接强转为Values通过getExpressions()拿到表达式列表并获取数量。作为子查询Sub-query使用SELECT * FROM ( VALUES 1, 2, 3 ) ;AST 结构SQL Text └─Statements: statement.select.PlainSelect ├─selectItems: statement.select.SelectItem │ └─AllColumns: * └─fromItem: statement.select.ParenthesedSelect └─select: statement.select.Values └─ExpressionList: 1, 2, 3对应的 Java 迁移代码PlainSelect select (PlainSelect) CCJSqlParserUtil.parse(sqlStr); ParenthesedSelect subSelect (ParenthesedSelect) select.getFromItem(); Values values (Values) subSelect.getSelect(); assertEquals( 3, values.getExpressions().size() );注意子查询外层由ParenthesedSelect包装旧的SubSelect已被移除见下文通过subSelect.getSelect()获取其内部真正的Select对象并转型为Values。作为表达式使用UPDATE test SET ( a , b , c ) ( VALUES 1, 2, 3 ) ;AST 结构SQL Text └─Statements: statement.update.Update ├─Table: test └─updateSets: statement.update.UpdateSet ├─ParenthesedExpressionList: (a, b, c) └─ExpressionList: (VALUES 1, 2, 3)对应的 Java 迁移代码Update update (Update) CCJSqlParserUtil.parse(sqlStr); UpdateSet updateSet update.getUpdateSets().get(0); ParenthesedSelect subSelect (ParenthesedSelect) updateSet.getValues().get(0); Values values (Values) subSelect.getSelect(); assertEquals( 3, values.getExpressions().size() );作为 Clause 使用INSERT 场景INSERT INTO test VALUES ( 1, 2, 3 ) ;AST 结构SQL Text └─Statements: statement.insert.Insert ├─Table: test └─select: statement.select.Values └─ParenthesedExpressionList: (1, 2, 3)对应的 Java 迁移代码Insert insert (Insert) CCJSqlParserUtil.parse(sqlStr); Values values (Values) insert.getSelect(); Assertions.assertEquals(3, values.getExpressions().size());这里insert.getSelect()返回的正是Values印证了Values直接充当Insert语句内部select节点的设计。二、ExpressionList从“包装器”回归真正的List4.7 之前ExpressionList内部包装了一个ListExpression访问元素需要调用getExpressions()。4.7 起ExpressionList直接继承java.util.ArrayListT自身就是一个真正的 List。源码证据见 ExpressionList.javapublic class ExpressionListT extends Expression extends ArrayListT implements Expression, Serializable。同时保留了三个标记Deprecated的兼容方法isUsingBrackets()、getExpressions()、setExpressions(List)便于旧代码逐步过渡但新代码不应再使用它们。该类的 Javadoc 也给出了典型使用场景SELECT A FROM TAB WHERE B IN (expr1,expr2,expr3)即IN列表、函数参数、GROUP BY 列等凡是“一组表达式”的位置都使用ExpressionList。基本用法示例SELECT Function( a, b, c ) FROM dual GROUP BY a , b , c ;AST 结构SQL Text └─Statements: statement.select.PlainSelect ├─selectItems: statement.select.SelectItem │ └─expression: expression.Function │ └─ExpressionList: a, b, c ├─Table: dual └─groupBy: statement.select.GroupByElement └─ExpressionList: a, b, c对应的 Java 迁移代码PlainSelect select (PlainSelect) CCJSqlParserUtil.parse(sqlStr); Function function (Function) select.getSelectItem(0).getExpression(); assertEquals(3, function.getParameters().size()); ExpressionList? groupByExpressionsselect.getGroupBy().getGroupByExpressionList(); assertEquals(3, groupByExpressions.size());因为ExpressionList就是Listfunction.getParameters()和groupByExpressions.size()都可以直接调用 List 的 API不再需要先取getExpressions()。Wrapped ExpressionList嵌套表达式列表SELECT ( ( 1, 2, 3 ), ( 4, 5, 6 ), ( 7, 8, 9 ) ) ;AST 结构SQL Text └─Statements: statement.select.PlainSelect └─selectItems: statement.select.SelectItem └─ParenthesedExpressionList: ((1, 2, 3), (4, 5, 6), (7, 8, 9))对应的 Java 迁移代码PlainSelect select (PlainSelect) CCJSqlParserUtil.parse(sqlStr); ParenthesedExpressionList? expressionList (ParenthesedExpressionList?) select.getSelectItem(0).getExpression(); ParenthesedExpressionList? expressionList1 (ParenthesedExpressionList?) expressionList.get(0); assertEquals(3, expressionList1.size());外层是ParenthesedExpressionList其元素仍然是ParenthesedExpressionList多层嵌套时逐层转型即可。移除的旧类与替换关系旧类4.7 前新替代4.7 起说明ItemsListExpressionList任何ListExpression都被视为反模式Anti Pattern一律改用ExpressionListMultiExpressionListExpressionList元素也是ExpressionList多行列表通过嵌套ExpressionList表达MultiExpressionList被移除后原来表示“多行值列表”如INSERT ... VALUES (1,2),(3,4)的结构改为由“元素同样是ExpressionList的ExpressionList”承载即外层的每个元素又是一个表达式列表。三、泛型SelectItem派生类全部合并SelectItemT extends Expression现在是泛型类原先的各种派生类——SelectExpressionItem、FunctionItem、ExpressionListItem等——已全部移除统一由SelectItem一个类承担。源码见 SelectItem.javapublic class SelectItemT extends Expression extends ASTNodeAccessImpl内部持有T expression与Alias alias并提供大量便捷构造器支持Long/Integer/Double/String直接构造内部自动包装为对应的LongValue、DoubleValue、StringValue以及静态工厂方法SelectItem.from(Expression, Alias)。迁移时原来的SelectExpressionItem直接替换为SelectItem?或带具体泛型的SelectItemColumn等。4.7 中还新增了类型安全的取值辅助方法getExpression(ClassE type)可直接从任意SelectItem中取出指定类型的表达式// 从 SelectItem 中直接取 LongValue LongValue lv select.getSelectItem(0).getExpression(LongValue.class); Assertions.assertEquals(1L, lv.getValue());四、Select语句三大旧类被吸收4.7 对Select家族做了大幅简化核心是让“选择子句”本身成为一个统一的多态节点树。SelectBody被移除PlainSelect直接可用旧的SelectBody抽象层被删除PlainSelect可以直接作为Select使用不再需要select.getSelectBody()这类中转调用。凡是原来返回SelectBody的地方现在直接返回Select运行时就是PlainSelect。SubJoin被ParenthesedFromItem取代括号内的连接join子句不再用独立的SubJoin表示而是统一用ParenthesedFromItem——它实现FromItem接口内部包含一个FromItem和一组常规的Join列表。源码见 ParenthesedFromItem.java提供了getFromItem()、getJoins()、getJoin(int index)、addJoins(Join...)等 APIappendTo输出时以括号包裹。SubSelect被移除任何Select都可作为子查询旧的SubSelect类被删除任何Select实现PlainSelect、Values或SetOperationList都可以直接充当子查询节点。子查询外层统一由ParenthesedSelect包装——见 ParenthesedSelect.java它实现了FromItem与ParenthesedStatement内部持有Select select并提供三个便捷的转型方法getPlainSelect()、getValues()、getSetOperationList()分别对应内部实际是哪种Select子类型。综合示例SetOperationList 混用三种 Select( SELECT * FROM ( SELECT 1 ) UNION ALL SELECT * FROM ( VALUES 1, 2, 3 ) UNION ALL VALUES ( 1, 2, 3 ) ) ;AST 结构SQL Text └─Statements: statement.select.ParenthesedSelect └─select: statement.select.SetOperationList ├─selects: statement.select.PlainSelect │ ├─selectItems: statement.select.SelectItem │ │ └─AllColumns: * │ └─fromItem: statement.select.ParenthesedSelect │ └─select: statement.select.PlainSelect │ └─selectItems: statement.select.SelectItem │ └─LongValue: 1 ├─selects: statement.select.PlainSelect │ ├─selectItems: statement.select.SelectItem │ │ └─AllColumns: * │ └─fromItem: statement.select.ParenthesedSelect │ └─select: statement.select.Values │ └─ExpressionList: 1, 2, 3 ├─selects: statement.select.Values │ └─ParenthesedExpressionList: (1, 2, 3) ├─UnionOp: UNION ALL └─UnionOp: UNION ALL对应的 Java 迁移代码ParenthesedSelect parenthesedSelect (ParenthesedSelect) CCJSqlParserUtil.parse(sqlStr); SetOperationList setOperationList parenthesedSelect.getSetOperationList(); PlainSelect select1 (PlainSelect) setOperationList.getSelect(0); PlainSelect subSelect1 ((ParenthesedSelect) select1.getFromItem()).getPlainSelect(); Assertions.assertEquals( 1L, subSelect1.getSelectItem(0).getExpression(LongValue.class).getValue()); Values values (Values) setOperationList.getSelect(2); Assertions.assertEquals( 3, values.getExpressions().size());这段代码清晰地展示了新模型SetOperationList.getSelect(i)返回的每个元素运行时可以是PlainSelect或ValuesParenthesedSelect是子查询的统一包装getExpression(LongValue.class)是泛型SelectItem提供的类型安全取值方式。Join 嵌套示例ParenthesedFromItem的应用SELECT * FROM a INNER JOIN ( b LEFT JOIN c ON b.d c.d ) ON a.e b.e ;AST 结构SQL Text └─Statements: statement.select.PlainSelect ├─selectItems: statement.select.SelectItem │ └─AllColumns: * ├─Table: a └─joins: statement.select.Join ├─rightItem: statement.select.ParenthesedFromItem │ ├─Table: b │ └─joins: statement.select.Join │ ├─Table: c │ └─onExpressions: expression.operators.relational.EqualsTo │ ├─Column: b.d │ └─Column: c.d └─onExpressions: expression.operators.relational.EqualsTo ├─Column: a.e └─Column: b.e对应的 Java 迁移代码PlainSelect select (PlainSelect) CCJSqlParserUtil.parse(sqlStr); Table aTable (Table) select.getFromItem(); ParenthesedFromItem fromItem (ParenthesedFromItem) select.getJoin(0).getFromItem(); Table bTable (Table) fromItem.getFromItem(); Join join fromItem.getJoin(0); Table cTable (Table) join.getFromItem(); assertEquals(c, cTable.getName());旧的subJoin.getJoinList()、subJoin.getFromItem()等访问方式统一改为ParenthesedFromItem的getFromItem()与getJoin(index)。五、Brackets从“布尔标志”到“显式包装节点”4.7 之前很多节点用hasBrackets()、isUsingBrackets()之类的布尔方法表示“外部是否带括号”信息零散且容易丢失。4.7 移除了这类方法改为解析器遇到括号时直接返回一个包装节点——ParenthesedExpressionList表达式列表带括号、ParenthesedSelect子查询带括号、ParenthesedFromItemFROM 项带括号或Parenthesis单个表达式带括号。这种设计让括号本身成为 AST 的一部分处理括号相关的语义例如括号对IN列表、复合表达式优先级的影响更加精确。注意 4.7 源码中ExpressionList.isUsingBrackets()仍存在但已标记Deprecated且固定返回false仅供兼容旧代码不应在新代码中使用。示例( SELECT ( 1 ) ) ;AST 结构SQL Text └─Statements: statement.select.ParenthesedSelect └─select: statement.select.PlainSelect └─selectItems: statement.select.SelectItem └─expression: expression.Parenthesis └─LongValue: 1对应的 Java 迁移代码ParenthesedSelect parenthesedSelect (ParenthesedSelect) CCJSqlParserUtil.parse(sqlStr); SetOperationList setOperationList parenthesedSelect.getSetOperationList(); PlainSelect select1 (PlainSelect) setOperationList.getSelect(0); PlainSelect subSelect1 ((ParenthesedSelect) select1.getFromItem()).getPlainSelect(); Assertions.assertEquals( 1L, subSelect1.getSelectItem(0).getExpression(LongValue.class).getValue()); Values values (Values) setOperationList.getSelect(2); Assertions.assertEquals( 3, values.getExpressions().size());六、UpdateSet子句SET 列表统一建模Insert、Update、Upsert、Merge语句中的任何SET子句4.7 起统一由ListUpdateSet表达。源码见 UpdateSet.java每个UpdateSet内部持有ExpressionListColumn columns与ExpressionListExpression values两个列表add(Column)/add(Expression)方法会在列表已有元素时自动把普通ExpressionList升级为ParenthesedExpressionList以正确还原 SQL 中“括号包裹的多列/多值”写法。UPDATE a SET ( a , b , c ) ( 1 , 2 , 3 ) , d 4 ;AST 结构SQL Text └─Statements: statement.update.Update ├─Table: a ├─updateSets: statement.update.UpdateSet │ ├─ParenthesedExpressionList: (a, b, c) │ └─ParenthesedExpressionList: (1, 2, 3) └─updateSets: statement.update.UpdateSet ├─ExpressionList: d └─ExpressionList: 4对应的 Java 迁移代码Update update (Update) CCJSqlParserUtil.parse(sqlStr); UpdateSet updateSet1 update.getUpdateSet(0); Assertions.assertEquals( 3, updateSet1.getColumns().size()); Assertions.assertEquals( 3, updateSet1.getValues().size()); UpdateSet updateSet2 update.getUpdateSet(1); Assertions.assertEquals( d, updateSet2.getColumn(0).getColumnName()); Assertions.assertEquals( 4L, ((LongValue) updateSet2.getValue(0)).getValue() );Update.getUpdateSets()返回整个列表getUpdateSet(int)按索引取单个见 Update.java 中的实现。迁移时注意updateSet1.getValues()返回的每个元素可能是ParenthesedSelect如前述 VALUES 表达式场景、LongValue、JdbcParameter等任意Expression需要按实际 SQL 形态转型。七、Statements集合直接继承ListStatement与ExpressionList同理Statements类现在直接继承ArrayListStatementStatements.getStatements()已经废弃。见 Statements.javapublic class Statements extends ArrayListStatement implements Serializable其中getStatements()仍保留但返回this仅供旧代码兼容。Statements statements CCJSqlParserUtil.parseStatements(sql); // 新写法直接当作 List 使用 for (Statement statement : statements) { // ... } // 旧写法已废弃statements.getStatements().get(0) Statement first statements.get(0);八、迁移核对清单与常见陷阱基于上文七个变更点给出 4.7 迁移时的核对清单VALUES 相关把ValueListExpression/ValuesStatement的引用全部替换为Values并根据使用位置顶层语句 / 子查询 / 表达式 / INSERT 子句选择对应的 AST 访问路径表达式列表删除所有xxx.getExpressions()中转调用直接用ExpressionList自身作为List不要再用ListExpression声明“一组表达式”统一用ExpressionList这是官方明确的反模式界定SelectItem将SelectExpressionItem、FunctionItem等派生类替换为泛型SelectItemT推荐结合getExpression(ClassE)做类型安全取值Select 家族删除selectBody中转子查询由ParenthesedSelect包装并转型括号连接用ParenthesedFromItem括号语义不要再用hasBrackets()/isUsingBrackets()判断括号改由ParenthesedExpressionList、ParenthesedSelect、ParenthesedFromItem、Parenthesis等包装节点的存在性来推断SET 子句Update、Insert、Upsert、Merge的 SET 全部改为ListUpdateSet并注意多列场景下UpdateSet内部会自动使用ParenthesedExpressionListStatementsgetStatements()已废弃直接按ListStatement遍历。常见陷阱提醒Values同时是Select与FromItem在FROM (VALUES ...)场景下需要一层ParenthesedSelect包装不能直接把Values当作fromItem强转UpdateSet.getValues()返回的是ExpressionList?其中元素类型随 SQL 形态变化ParenthesedSelect、字面量、参数占位符等务必按实际内容转型。总结JSqlParser 4.7 的 AST 重写本质上是一次“去冗余、统一化”的改造Values一统 VALUES 语义ExpressionList回归标准 List 形态SelectItem泛型化收编派生类ParenthesedSelect/ParenthesedFromItem/Parenthesis让括号成为显式 AST 节点UpdateSet统一 SET 子句建模Statements直接作为集合使用。按照本文的替换表逐项迁移同时留意括号包装层与运行时类型转型这两处最易出错的地方即可平滑完成升级。4.7 之后该模型延续到 5.0 系列——后者在此基础之上进一步引入了泛型 Context 的访问器Visitor体系与叶子到根节点的泛型返回值机制见 src/site/sphinx/migration50.rst理解 4.7 的这些 AST 结构变化正是掌握后续版本新模型的前提。赞分享数据库【免费下载链接】JSqlParserJSqlParser parses an SQL statement and translate it into a hierarchy of Java classes. The generated hierarchy can be navigated using the Visitor Pattern项目地址https://gitcode.com/gh_mirrors/js/JSqlParser点击查看免费下载相关推荐JSQLParser 5.0 迁移指南泛型化 AST Visitor 与上下文传递的破坏性变更JSQLParser 5.0 迁移指南泛型化 AST Visitor 与上下文传递的破坏性变更 导读 JSQLParser 5.0 是一次面向 AST 遍历模数据库LLMCompiler智慧银行系统多渠道交易并行处理与风险监控LLMCompiler智慧银行系统多渠道交易并行处理与风险监控 在金融科技飞速发展的今天银行系统面临着日益复杂的交易场景和严格的风险管控需求。LLMComp人工智能大模型工具调用H3 v1 到 v2 迁移指南全面解读 API 变更、Web 标准重写与代码迁移方案H3 v1 到 v2 迁移指南全面解读 API 变更、Web 标准重写与代码迁移方案 导读H3 v2 是一次以 Web 标准原语 URL 、 Header后端Web框架上一篇OpCore-Simplify10分钟搞定黑苹果EFI配置告别3天调试噩梦下一篇如何用WeChatMsg永久保存微信聊天记录你的数字记忆守护者创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考