求助:使用ANTLR4解析MySQL查询并提取组件的简单示例
我完全懂你现在的困惑——用Maven把ANTLR4的MySQL语法生成了Java类,但对着一堆自动生成的代码,完全不知道怎么下手解析SQL、提取那些像选中列、表名、WHERE条件这些关键组件对吧?别担心,我给你一个简单又完整的示例,一步步带你搞定核心逻辑。
核心思路:用ANTLR Visitor遍历语法树
ANTLR生成的Parser会把SQL转换成一棵语法树,我们要做的就是遍历这棵树,在对应的节点上提取我们需要的信息。最灵活的方式是用Visitor模式——继承ANTLR自动生成的MySQLBaseVisitor,重写对应语法规则的访问方法,在方法里收集数据。
第一步:自定义Visitor类(收集我们需要的信息)
这个类负责遍历语法树,把选中列、表名、WHERE条件这些信息存起来:
import org.antlr.v4.runtime.tree.ParseTree; import java.util.ArrayList; import java.util.List; public class MySQLQueryExtractor extends MySQLBaseVisitor<Void> { // 存储提取到的选中列 private final List<String> selectedColumns = new ArrayList<>(); // 存储表名 private final List<String> tableNames = new ArrayList<>(); // 存储WHERE条件内容 private String whereCondition; // 访问SELECT语句的顶层节点 @Override public Void visitSelectStatement(MySQLParser.SelectStatementContext ctx) { // 调用父类方法,自动遍历所有子节点 return super.visitSelectStatement(ctx); } // 访问每个SELECT列项(比如id、name、email) @Override public Void visitSelectItem(MySQLParser.SelectItemContext ctx) { String columnText = ctx.getText(); selectedColumns.add(columnText); return super.visitSelectItem(ctx); } // 访问表名节点 @Override public Void visitTableName(MySQLParser.TableNameContext ctx) { String tableText = ctx.getText(); tableNames.add(tableText); return super.visitTableName(ctx); } // 访问WHERE子句节点 @Override public Void visitWhereClause(MySQLParser.WhereClauseContext ctx) { // 去掉"WHERE "前缀,只保留条件内容 whereCondition = ctx.getText().substring(6).trim(); return super.visitWhereClause(ctx); } // 对外提供获取提取结果的方法 public List<String> getSelectedColumns() { return selectedColumns; } public List<String> getTableNames() { return tableNames; } public String getWhereCondition() { return whereCondition; } }
第二步:编写主程序解析SQL
这部分负责初始化ANTLR的Lexer、Parser,生成语法树,然后用我们的自定义Visitor去遍历提取信息:
import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTree; public class SQLParserDemo { public static void main(String[] args) { // 测试用的SQL语句 String testSql = "SELECT id, name, email FROM users WHERE age > 18 AND status = 'active'"; // 1. 把SQL转换成ANTLR能处理的字符流 CharStream charStream = CharStreams.fromString(testSql); // 2. 用Lexer把字符流转换成Token(比如关键字、标识符、运算符这些) MySQLLexer lexer = new MySQLLexer(charStream); // 3. 用TokenStream管理Token序列 CommonTokenStream tokenStream = new CommonTokenStream(lexer); // 4. 用Parser把Token序列生成语法树 MySQLParser parser = new MySQLParser(tokenStream); // 5. 获取语法树的根节点(这里对应SELECT语句的顶层规则) ParseTree parseTree = parser.selectStatement(); // 6. 用自定义Visitor遍历语法树,提取信息 MySQLQueryExtractor extractor = new MySQLQueryExtractor(); extractor.visit(parseTree); // 输出提取结果 System.out.println("提取的选中列: " + extractor.getSelectedColumns()); System.out.println("提取的表名: " + extractor.getTableNames()); System.out.println("提取的WHERE条件: " + extractor.getWhereCondition()); } }
一些关键说明和扩展提示
- 怎么找要重写的方法? 看自动生成的
MySQLParser类,里面所有以Context结尾的内部类,对应语法文件里的规则。比如SubqueryContext对应子查询规则,如果你要提取子查询,就重写visitSubquery方法。 - 处理更复杂的SQL:比如带别名的表(
user u)、JOIN、函数调用(COUNT(id)),你需要重写对应的规则方法,比如TableReferenceContext来处理带别名的表,FunctionCallContext来处理函数列。 - 调试小技巧:可以用ANTLR的可视化工具(antlr4-gui)把语法树画出来,直观看到每个节点对应的规则,这样就知道该重写哪个方法了。
- 确保生成的类在classpath里:Maven生成的ANTLR类默认在
target/generated-sources/antlr4目录,要让IDE把这个目录标记为源目录,不然会找不到MySQLLexer、MySQLParser这些类。
内容的提问来源于stack exchange,提问作者KCK
相关产品推荐
相关产品推荐

