我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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时,无法正确解析出所有的表别名(也可能是我没理解到位...😅)。重写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

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录