JSQLParser 解析 复杂sql (表别名、字段与表对应关系)
更新:
最新代码在这里:https://blog.csdn.net/m0_54892309/article/details/129615905
增加了对于嵌套SQL语句的解析,并改进了相关代码~~~
正文:
最近在搞一个公司自研的数据中台项目,许多模块都有解析sql的需求。于是乎,开发一个能完美解析sql语句的工具类已经是迫在眉睫了!
到网上百度了两下,便发现了JSQLParser这个免费好用的工具类,相信很多朋友早就在用了吧~~~
话不多说,先来了解下JSQLParser里的两个主要工具类吧。
工具类 | 功能 | |
1 | CCJSqlParserUtil | 只能解析简单sql语句 |
2 | CCJSqlParserManager | 正确语法的sql都能解析 |
可以发现,CCJSqlParserUtil这个东西虽然简单好用功能强大精确无误(省略1000字),但是只能解析单表查询的简单sql,也就是说对于有子查询的sql是会直接报错的。
CCJSqlParserManager才是符合业务需求的真正好用的工具类,尽管它用起来确实麻烦,各种Expression表达式的解析,还有visit方法的重写,都是需要深刻理解才能用好的。
关于JSQLParser的基本语法网上都有,这里就不在赘述了。 在学习使用的过程中,我发现使用CCJSqlParserManager这个类去解析复杂sql时,无法正确解析出所有的表别名(也可能是我没理解到位...😅)。重写JSQLParser的visit方法应该可以实现表别名的解析,我这里就用自己比较能接受的方式来了。
实战环节:
maven依赖
com.github.jsqlparser jsqlparser 4.4
实体类
NormalSqlStructureDto.class
private String sql; private List tableNames; private List selectItems; private List colMappings;
ColMappingDto.class
private String name; private String alias; private Object table; private String type;
主要代码
public class JsqlParserUtil { public static void main(String[] args) throws JSQLParserException { // 输入一个sql String sql = "select t11.*,t1.* \n" + "from original_data.edu_college_student As t1\n" + "JOIN original_data.edu_college_test_score t11\n" + "on t1.s_id = t11.s_id \n" + "where 1=1 \n"; NormalSqlStructureDto normalSqlStructureDto = getStructure(sql.replace("\r", " ").replace("\n", " "), true); normalSqlStructureDto.getTableNames().forEach(System.out::println); System.out.println("==============================================="); normalSqlStructureDto.getSelectItems().forEach(System.out::println); System.out.println("end"); } private static void buildTblMapping(Map tableMapping, String sql, String tblAlias) { if (StringUtils.isNotEmpty(tblAlias)) { if (CollectionUtils.isEmpty(tableMapping) || Objects.isNull(tableMapping.get(tblAlias))) { sql = sql.replaceAll("(?i)\\s+as\\s+", " "); String regex = "(from|join)\\s+(\\w+\\.)?\\w+\\s+".concat(tblAlias).concat("\\s+"); Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(sql.replaceAll("[\n\r]", " ")); String replaceReg = "(?i)(from|join|" + tblAlias + ")"; while (m.find()) { tableMapping.put(tblAlias, m.group(0).replaceAll(replaceReg, "").trim()); } } } public static NormalSqlStructureDto getStructure(String sql, boolean isAlias) throws ServiceException, JSQLParserException { NormalSqlStructureDto normalSqlStructureDto = new NormalSqlStructureDto(); if (StringUtils.isEmpty(sql)) { throw new ServiceException("请先输入SQL语句"); } normalSqlStructureDto.setSql(sql); sql = sql.replaceAll("(\\$\\{\\w*\\})|(\\{\\{\\w+\\}\\})", "''"); // 1.解析表名 CCJSqlParserManager parserManager = new CCJSqlParserManager(); // 解析SQL为Statement对象 Statement statement = parserManager.parse(new StringReader(sql)); // 创建表名发现者对象 TablesNamesFinder tablesNamesFinder = new TablesNamesFinder(); // 获取到表名列表 List tableNameList = tablesNamesFinder.getTableList(statement); normalSqlStructureDto.setTableNames(tableNameList); // 表别名映射 Map tableMapping = new HashMap<>(); tableNameList.forEach(i -> tableMapping.put(i, i)); // 字段和表的映射 List colMappingList = new ArrayList<>(); // 2.解析查询元素 列,函数等 Select select = (Select) CCJSqlParserUtil.parse(sql); PlainSelect plainSelect = (PlainSelect) select.getSelectBody();// FromItem fromItem = plainSelect.getFromItem();// System.out.println(JSON.toJSON(fromItem).toString());// fromItem.getAlias(); List selectItems = plainSelect.getSelectItems(); List columnList = new ArrayList<>(); if (!CollectionUtils.isEmpty(selectItems)) { for (SelectItem selectItem : selectItems) { ColMappingDto colMapping = new ColMappingDto(); String columnName = ""; String tblAlias = ""; try { if (selectItem instanceof SelectExpressionItem) { SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem; Alias alias = selectExpressionItem.getAlias(); Expression expression = selectExpressionItem.getExpression(); // FIXME: 2023/3/9 Column col = ((Column) expression); Table colTbl = col.getTable(); if (Objects.nonNull(colTbl)) {tblAlias = colTbl.getName(); } buildTblMapping(tableMapping, sql, tblAlias); if (!isAlias) {columnName = selectItem.toString(); } else if (expression instanceof CaseExpression) {// case表达式columnName = alias.getName(); } else if (expression instanceof LongValue || expression instanceof StringValue || expression instanceof DateValue || expression instanceof DoubleValue) {// 值表达式columnName = Objects.nonNull(alias.getName()) ? alias.getName() : expression.getASTNode().jjtGetValue().toString(); } else if (expression instanceof TimeKeyExpression) {// 日期columnName = alias.getName(); } else {if (alias != null) { columnName = alias.getName();} else { SimpleNode node = expression.getASTNode(); Object value = node.jjtGetValue(); if (value instanceof Column) { columnName = ((Column) value).getColumnName(); } else if (value instanceof Function) { columnName = value.toString(); } else { // 增加对select 'aaa' from table; 的支持 columnName = String.valueOf(value); columnName = columnName.replace("'", ""); columnName = columnName.replace("\"", ""); columnName = columnName.replace("`", ""); }} } columnName = columnName.replace("'", ""); columnName = columnName.replace("\"", ""); columnName = columnName.replace("`", ""); colMapping.setName(col.getColumnName()); if (Objects.nonNull(alias) && StringUtils.isNotEmpty(alias.getName())) {colMapping.setAlias(alias.getName()); } colMapping.setTable(tableMapping.get(tblAlias)); } else if (selectItem instanceof AllTableColumns) { AllTableColumns allTableColumns = (AllTableColumns) selectItem; columnName = allTableColumns.toString(); if (columnName.indexOf(".") > -1) {tblAlias = columnName.substring(0, columnName.indexOf(".")).trim();buildTblMapping(tableMapping, sql, tblAlias);colMapping.setTable(tableMapping.get(tblAlias)); } else {colMapping.setTable(tableNameList); } colMapping.setName(columnName); } else if (selectItem.toString().equals("*")) { columnName = selectItem.toString(); colMapping.setName(columnName); colMapping.setTable(tableNameList); } else { columnName = selectItem.toString(); colMapping.setName(columnName); colMapping.setType("varchar"); } } catch (Exception e) { columnName = selectItem.toString(); colMapping.setName(columnName); colMapping.setType("varchar"); colMapping.setTable(null); } columnList.add(columnName); colMappingList.add(colMapping); } normalSqlStructureDto.setSelectItems(columnList); normalSqlStructureDto.setColMappings(colMappingList); } return normalSqlStructureDto; } }
参考:https://blog.csdn.net/qq_41541619/article/details/104576427这篇博客,讲得非常详细。
来源地址:https://blog.csdn.net/m0_54892309/article/details/129487931
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341