返回顶部
首页 > 资讯 > 后端开发 > JAVA >JSQLParser 解析 复杂sql (表别名、字段与表对应关系)
  • 825
分享到

JSQLParser 解析 复杂sql (表别名、字段与表对应关系)

javasqlPoweredby金山文档 2023-09-04 07:09:50 825人浏览 独家记忆
摘要

更新: 最新代码在这里:https://blog.csdn.net/m0_54892309/article/details/129615905 增加了对于嵌套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时,无法正确解析出所有的表别名(也可能是我没理解到位...😅)。重写JSQLParservisit方法应该可以实现表别名的解析,我这里就用自己比较能接受的方式来了。

实战环节:

  1. Maven依赖

                            com.GitHub.jsqlparser            jsqlparser            4.4        

  1. 实体类

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;

  1. 主要代码

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

--结束END--

本文标题: JSQLParser 解析 复杂sql (表别名、字段与表对应关系)

本文链接: https://lsjlt.com/news/393132.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作