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

这篇具有很好参考价值的文章主要介绍了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依赖

        <!-- sql解析 工具 jsqlparser -->
        <dependency>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
            <version>4.4</version>
        </dependency>
  1. 实体类

NormalSqlStructureDto.class

    /** SQL语句 */
    private String sql;
    /** 表名 */
    private List<String> tableNames;
    /** 检索项 */
    private List<String> selectItems;
    /** 字段和表的映射关系 */
    private List<ColMappingDto> 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");
    }

    /**
     * 构建表名和表别名的对应关系
     *
     * @param tableMapping
     * @param sql
     * @param tblAlias
     */
    private static void buildTblMapping(Map<String, Object> 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());
                }
            }
        }

    /**
     * 解析sql结构
     *
     * @param sql
     * @param isAlias true|false 是否使用别称<br> eg. 【s_id as id】 => 【id】<br>
     * @return
     * @throws ServiceException
     * @throws JSQLParserException
     */
    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<String> tableNameList = tablesNamesFinder.getTableList(statement);
        normalSqlStructureDto.setTableNames(tableNameList);
        // 表别名映射
        Map<String, Object> tableMapping = new HashMap<>();
        tableNameList.forEach(i -> tableMapping.put(i, i));
        // 字段和表的映射
        List<ColMappingDto> 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<SelectItem> selectItems = plainSelect.getSelectItems();
        List<String> 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://www.toymoban.com/news/detail-430503.html

到了这里,关于JSQLParser 解析 复杂sql (表别名、字段与表对应关系)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Mybatis Plus之DQL(条件查询方式、查询投影、查询条件设定、字段映射与表名映射)

    增删改查四个操作中,查询是非常重要的也是非常复杂的操作,这块需要我们重点学习下,这节我们主要学习的内容有: 条件查询方式 查询投影 查询条件设定 字段映射与表名映射 1.1 条件查询的类 MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的

    2024年02月05日
    浏览(45)
  • 【手写数据库toadb】表relation访问实现概述,分层设计再实践,表访问层与表操作层简化代码复杂度

    ​ 专栏内容 : 手写数据库toadb 本专栏主要介绍如何从零开发,开发的步骤,以及开发过程中的涉及的原理,遇到的问题等,让大家能跟上并且可以一起开发,让每个需要的人成为参与者。 本专栏会定期更新,对应的代码也会定期更新,每个阶段的代码会打上tag,方便阶段学

    2024年02月22日
    浏览(40)
  • java使用jsqlparser实现入参,并生成可执行sql

    话不多说,直接上 验证通过的代码 执行结果如下: 通过多个单条sql,生成关联sql,也就是 join on 执行结果:

    2024年02月14日
    浏览(26)
  • MySQL查询——为表和字段取别名

    在前面介绍分组查询、集合函数查询和嵌套子查询内容中,有的地方使用 AS 关键 字为查询结果中的某一列指定一个特定的名字。在内连接查询时,则对相同的表 fruits 分别指定两 个不同的名字,这里可以为字段或者表取一个别名,在查询时,使用别名替代其指定的内容,下

    2024年02月09日
    浏览(38)
  • Elasticsearch 字段别名 field-alias

    Elasticsearch 8.1 Kibana 8.1 MacOS 10.14.6 首先我们还是先了解一下, 什么是字段别名? 大家可能听说过 索引别名 ,通过索引的别名可以轻松的切换所需的数据来源与哪一个索引,那么什么是字段别名呢?所谓字段别名,就是索引mapping定义时的备用字段,通过字段别名可以替换搜索

    2023年04月18日
    浏览(62)
  • 【Java反序列化】@JsonAlias字段别名

       @JsonAlias 是 Jackson 库提供的一个注解,用于在反序列化 JSON 数据时,为字段或方法指定多个可接受的名称或别名。   当你使用 @JsonAlias 注解时,可以为字段或方法指定一个或多个别名。这样,当 Jackson 反序列化 JSON 数据时,它将尝试使用给定的别名来匹配字段或方法的

    2024年02月16日
    浏览(32)
  • Elasticsearch 索引管理:使用别名来修改字段类型

    在 Elasticsearch 中,一个常见的问题是如何修改已存在的索引的字段类型。这是一个棘手的问题,因为 Elasticsearch 本身不允许直接修改字段类型。如果删除现有索引,重新建索引的话则会导致数据丢失。有一个方法是使用别名索引,当需要调整索引时可以先新建一个索引,把数

    2024年02月03日
    浏览(35)
  • 【ArcGIS Pro二次开发】(63):批量更改字段别名

    在我工作中遇到的大多数图斑,字段名称一般是英文,字段别名是中文,使用起来是比较方便的。 但有时候也会遇到一些不一样的情况,不知是经过了怎样的处理,图斑的字段别名被修改成了和字段名称一样的英文,这样就很理解字段名称的意思,特别是其它专业、不熟悉的

    2024年02月11日
    浏览(60)
  • CC工具箱使用指南:【更改字段别名(属性映射)】

    一、简介 在我工作中遇到的大多数图斑,字段名称一般是英文,字段别名是中文,使用起来是比较方便的。 但有时候数据经过分析处理过后,图斑的字段别名被修改成了和字段名称一样的英文,这样就很难理解字段名称的意思,特别是其它专业、不熟悉的图斑,就很麻烦。

    2024年01月18日
    浏览(38)
  • 列化复杂的xml对应的类

    好的,可以试着为一个复杂的XML生成对应的C#类。这里我就简单的XML为例: 针对上述XML,我们可以使用C#中的XML序列化特性生成类定义: 这里通过XmlRoot, XmlElement和XmlArray等特性,我们可以清晰的定义出XML与类的映射关系。 如果XML结构更加复杂,包含更多层级关系,同样可以使用嵌套的

    2024年02月09日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包