IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> SQL解析处理和扩展开发工具 Java -> 正文阅读

[大数据]SQL解析处理和扩展开发工具 Java

Druid_SQL_AST

AST是abstract syntax tree的缩写,也就是抽象语法树。和所有的Parser一样,Druid Parser会生成一个抽象语法树。
本文重点不是讲解AST,想要了解这方面的内容可以参见官方文档:
https://github.com/alibaba/druid/wiki/Druid_SQL_AST
本文主要是在接触Druid中发现其中有很多拿来即用的SQL相关工具组件,秉着学习积累的态度于是将这些组件进行整理记录。如果在之后有类似的需要可以拿来即用,或者深入了解后进行扩展开发。

导入依赖:

    <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.9</version>
        </dependency>

SQL解析

通过SQLUtils产生List,通过下面简单的代码就可以解析出SQL语句AST对象

 DbType dbType = JdbcConstants.MYSQL;
 String sql="select * from dual where 1=1 order by create_time desc limit 10,10";
 List<SQLStatement> statementList = SQLUtils.parseStatements(sql, dbType);
 for (SQLStatement sqlStatement : statementList) {
     System.out.println(sqlStatement);
 }

SQL翻译 MySQL语句翻译成Oracle

SQL 翻译,即将一种方言,翻译成另一种。比如输入 MySQL 的 SQL 脚本,使用 MySQL 的 Parser 进行解析,再使用 Oracle 的 Visitor 进行遍历输出,就可以完成 MySQL 脚本到 Oracle 脚本的翻译:


    /**
     *  SQL翻译,将MySQL语句翻译成Oracle
     * @author zhangyu
     * @date 2022/5/22
     **/
    @Test
    public void  test12(){
        // MySQL的语言
        String sql="select * from dual where 1=1 order by create_time desc limit 10,10";
        List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql);
        // 将MySQL方言翻译为Oracle的
        String oracleSql = SQLUtils.toSQLString(sqlStatements, DbType.oracle);
        System.out.println(oracleSql);
    }

在这里插入图片描述
Tips:上面只演示了MySQL转换Oracle的,其他的类型可以参考DbType这个枚举类。

SQL语句格式化

Druid SQL Parser提供了格式化代码的工具类。这个是基于语义分析做的SQL格式化功能,比其他的SQL格式化做的更智能,效果更好

package com.alibaba.druid.sql;
 
public class SQLUtils {
    String format(String sq, String dbType);
    String format(String sq, String dbType, FormatOption option);
}

其中dbType支持mysql/postgresql/odps/oracle/db2/sqlserver
option缺省有SQLUtils.DEFAULT_FORMAT_OPTION(大写)、SQLUtils.DEFAULT_LCASE_FORMAT_OPTION(小写)两种可以选择,也可按需要定制化。

    /**
     *  SQL格式化
     * @author zhangyu
     * @date 2022/5/22
     **/
    @Test
    public void test13(){
        String sql = "select t1.id ,t2.name  from t1 ,t2  where 1=1  and t1.id=t2.id and t2.name='ZY' order by create_time desc limit 10,10";
        String result = SQLUtils.format(sql, JdbcConstants.MYSQL);
        System.out.println(result); // 缺省大写格式
        System.out.println("-------------------------------------------------------");
        String result_lcase = SQLUtils.format(sql
                , JdbcConstants.MYSQL
                , SQLUtils.DEFAULT_LCASE_FORMAT_OPTION);
        System.out.println(result_lcase); // 小写格式
    }

在这里插入图片描述

SQL语句验证

SQLUtils.parseStatements会根据输入的SQL语句以及方言进行AST语法的分析,我们可以利用这个去判断验证SQL是否是正确的,比较简单的方式就是直接try-catch捕获异常。下面的实例中故意多写了一个where,解析失败


    /**
     *  验证SQL的正确性
     * @author zhangyu
     * @date 2022/5/22
     **/
    @Test
    public void test14(){
        String sql = "select t1.id ,t1.name  from t1  t2 where   ";
        try{
            List<SQLStatement> statementList = SQLUtils.parseStatements(sql, DbType.mysql);
            for (SQLStatement sqlStatement : statementList) {
                System.out.println(sqlStatement);
            }
        }catch (Exception e){
            log.error("解析SQL失败",e);
            e.printStackTrace();
        }
    }

获取表名、查询字段、查询条件

SQLUtils.parseStatements会根据输入的SQL语句以及方言进行AST语法的分析,在处理过程中会将SQL语句进行解析分析,并且提供了一些外部的访问方式去获取解析后的信息,结合特定方言的MySqlSchemaStatVisitor对象可以获取这些信息
在这里插入图片描述


    /**
     *
     *  获取表名、查询字段、查询条件
     */
    @Test
    public void test15(){
        DbType dbType = JdbcConstants.MYSQL;
        String sql = "select\n" +
                "    -- 注释\n" +
                "    t1.id,\n" +
                "    t2.name\n" +
                "from\n" +
                "    table1 t1,\n" +
                "    table2 t2,\n" +
                "    table3 t3\n" +
                "where\n" +
                "    t1.id = t2.user_id\n" +
                "    and t3.user_id = t1.id\n" +
                "    and t1.name = 'zy'\n" +
                "    and t2.address = '安徽合肥'";
        List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
        for (SQLStatement stmt : stmtList) {
            MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
            stmt.accept(visitor);
            // 获取表名
            System.out.println("获取表名");
            Map<TableStat.Name, TableStat> tables = visitor.getTables();
            Set<TableStat.Name> tableNameSet = tables.keySet();
            for (TableStat.Name name : tableNameSet) {
                String tableName = name.getName();
                System.out.println(tableName);
            }
            System.out.println("获取查询列名");
            // 获取查询列名
            Collection<TableStat.Column> columns = visitor.getColumns();
            for (TableStat.Column column : columns) {
                if (column.isSelect()) {
                    String table = column.getTable();
                    String name = column.getName();
                    System.out.println(table + "." + name);
                }
            }
            System.out.println("获取查询条件");
            // 获取查询条件
            List<TableStat.Condition> conditions = visitor.getConditions();
            for (TableStat.Condition condition : conditions) {
                TableStat.Column column = condition.getColumn();
                String table = column.getTable();
                String name = column.getName();
                String operator = condition.getOperator();
                System.out.println();
                System.out.print(table + "." + name + operator);
                List<Object> values = condition.getValues();
                for (Object value : values) {
                    System.out.print(value);
                }
            }
        }
    }

在这里插入图片描述

修改表名

所有的AST节点都支持Visitor模式,需要自定义逻辑,可以实现相应的ASTVisitorAdapter派生类,在下面的实例中我们自定义了CustomTableAliasVisitor 然后实现的方法是参数为SQLExprTableSource 。对应接口SQLASTVisitor存在很多空接口,我们可以根据需要修改表名信息,字段信息,关联信息等等,根据需求实现对应的方法。
在这里插入图片描述
【实例】在下面的实例中我修改了t2的表名为newTableName。这个应用在很多的场景都适用,比如查询逻辑变动,比如将表改成了视图,再比如分表后的一些查询实现等等

 /**
     * 修改表名
     */
    @Test
    public void test16() {
        DbType dbType = JdbcConstants.MYSQL;
        String sql = "select t1.name ,t2.name  from t1 ,t2 ";
        List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
        CustomTableAliasVisitor visitor = new CustomTableAliasVisitor();
        for (SQLStatement stmt : stmtList) {
            stmt.accept(visitor);
        }
        String sqlString = SQLUtils.toSQLString(stmtList, dbType);
        System.out.println(sqlString);

    }

    /**
     * 自定义处理器,自定义表名信息
     */
    static class CustomTableAliasVisitor extends MySqlASTVisitorAdapter {
        @Override
        public boolean visit(SQLExprTableSource x) {
            // 别名
            String alias = x.getAlias();
            // 表名
            String tableName = x.getTableName();
            if ("t2".equals(tableName)) {
                // 修改表2的表名
                x.setExpr("newTableName");
                //修改别名
                x.setAlias("t2");
            }
            return true;
        }
    }

在这里插入图片描述

为SQL增加额外的where条件

对于一些SQL场景下,希望手动添加额外的条件,那么可以通过 SQLUtils.addCondition的方式添加扩展条件

    /**
     * 为SQL添加额外的条件
     *
     * @author zhangyu
     * @date 2022/5/22
     **/
    @Test
    public void test2() {
        String sql = "select * from dual where 1=1 order by create_time desc limit 10,10";
        // 添加AND条件
        String s = SQLUtils.addCondition(sql, "address = 'AnHui HeFei' ", DbType.mysql);
        // 添加OR条件
        String s1 = SQLUtils.addCondition(s, "name = 'AnHui' ", SQLBinaryOperator.BooleanOr, false, DbType.mysql);
        System.out.println(s1);
    }

在这里插入图片描述

解析select项、表信息、条件信息


    @Test
    public void test17() {
        String sql = "select t1.name ,t2.address  from t1 ,t2  where t1.id=t2.user_id and t1.name =? and t2.name =?";
        //使用mysql解析
        MySqlStatementParser sqlStatementParser = new MySqlStatementParser(sql);
        //解析select查询
        SQLSelectStatement sqlStatement = (SQLSelectStatement) sqlStatementParser.parseSelect();
        SQLSelect sqlSelect = sqlStatement.getSelect();
        //获取sql查询块
        SQLSelectQueryBlock sqlSelectQuery = (SQLSelectQueryBlock) sqlSelect.getQuery();

        StringBuffer out = new StringBuffer();
        //创建sql解析的标准化输出
        SQLASTOutputVisitor sqlastOutputVisitor = SQLUtils.createFormatOutputVisitor(out, null, JdbcUtils.MYSQL);

        //解析select项
        out.delete(0, out.length());
        for (SQLSelectItem sqlSelectItem : sqlSelectQuery.getSelectList()) {
            if (out.length() > 1) {
                out.append(",");
            }
            sqlSelectItem.accept(sqlastOutputVisitor);
        }
        System.out.println("查询字段信息 " + out);

        //解析from
        out.delete(0, out.length());
        sqlSelectQuery.getFrom().accept(sqlastOutputVisitor);
        System.out.println("表信息 " + out);

        //解析where
        out.delete(0, out.length());
        sqlSelectQuery.getWhere().accept(sqlastOutputVisitor);
        System.out.println("查询条件信息 " + out);
    }

在这里插入图片描述

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-05-24 18:15:37  更:2022-05-24 18:16:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:39:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码