当前位置: 首页 > news >正文

Druid学习笔记 03、Druid的AstNode类详解与其他产品测试体验

文章目录

  • 前言
  • 解析ast操作
  • ast的结构(继承实现关系)
    • SQL类型四种结构实现(SQLStatement)
    • 可替换SQL组件类(SQLReplaceable接口实现)
    • 表数据源类(SQLTableSource)
    • 表达式实现类(SQLExpr)
    • 提示信息接口(SQLHint)
    • 查询实现接口(SQLSelectQuery)
  • 深入解析Statement(组成结构)
    • 顶层抽象类
    • SelectStatement
      • **SelectStatement实现类**
      • SQLSelect
      • SQLSelectQuery
  • 针对解析到的node树进行处理模板操作
  • 关于不同结构之间血缘如何计算(简单记录)
    • 复杂sql结合例子
      • 步骤一:确认目标表的字段
      • 步骤二:union右边的(带子查询、left join)
      • 步骤三:union左边的
    • 合并去看
  • datablau血缘问题记录
    • 问题1:复杂sql加上on条件之后血缘解析有问题
    • 问题2:union on问题
  • 资料获取

Druid学习笔记 03、Druid的AstNode类详解与其他产品测试体验

前言

博主介绍:✌目前全网粉丝4W+,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。

涵盖技术内容:Java后端、大数据、算法、分布式微服务、中间件、前端、运维等。

博主所有博客文件目录索引:博客目录索引(持续更新)

CSDN搜索:长路

视频平台:b站-Coder长路

解析ast操作

解析语句相对简单,wiki上直接有示例,如:

String dbType = JdbcConstants.MYSQL;
List<SQLStatement> statementList = SQLUtils.parseStatements(sql, dbType);

SQLUtils的parseStatements方法会把你传入的SQL语句给解析成SQLStatement对象集合,每一个SQLStatement代表一条完整的SQL语句,如:

SELECT id FROM user WHERE status = 1

多个SQLStatement如:

SELECT id FROM user WHERE status = 1;
SELECT id FROM order WHERE create_time > '2018-01-01'

一般上我们只处理一条语句。

ast的结构(继承实现关系)

SQL类型四种结构实现(SQLStatement)

SQLStatement表示一条SQL语句,我们知道常见的SQL语句有CRUD四种操作,所以SQLStatement会有四种主要实现类,如:

class SQLSelectStatement implements SQLStatement {SQLSelect select;
}class SQLUpdateStatement implements SQLStatement {SQLExprTableSource tableSource;List<SQLUpdateSetItem> items;SQLExpr where;
}class SQLDeleteStatement implements SQLStatement {SQLTableSource tableSource; SQLExpr where;
}class SQLInsertStatement implements SQLStatement {SQLExprTableSource tableSource;List<SQLExpr> columns;SQLSelect query;
}

这里我们以SQLSelectStatement来说明,ast既然是SQL的语法结构表示。

我们先看一下ast和SQL 增删改查语法的主要对应结构如下:

img

可替换SQL组件类(SQLReplaceable接口实现)

**介绍:**SQLReplaceable 接口允许你把一个 SQL 表达式或者对象替换成另外一个,这在 SQL 解析、优化以及转换的过程中很有用。借助这个接口,你可以对 SQL 语句里的特定部分进行修改或者替换,从而实现自定义的 SQL 处理逻辑。

**实际应用:**例如 SQLLimit 是用于限制查询结果数量的组件,SQLOrderBy 是用于对查询结果进行排序的组件。

小组件节点SQLSelectItem、SQLSelectGroupByClause、SQLOrderBy、SQLLimit:

img

表数据源类(SQLTableSource)

**介绍:**在 SQL 查询中,SQLTableSource 实现类通常会定义数据的出处,可能是数据库中的物理表、视图,或者是经过特殊处理后的虚拟表等。例如,一个实现 SQLTableSource 用于从 MySQL 数据库表获取数据的类,可以命名为 MySQLSQLTableDataSource 。

**实际应用:**SQLTableSource这个节点,它有着常见的实现SQLExprTableSource(from的表)、SQLJoinTableSource(join的表)、SQLSubqueryTableSource(子查询的表):

img

class SQLTableSourceImpl extends SQLObjectImpl implements SQLTableSource { String alias;
}// 例如 select * from emp where i = 3,这里的from emp是一个SQLExprTableSource
// 其中expr是一个name=emp的SQLIdentifierExpr
class SQLExprTableSource extends SQLTableSourceImpl {SQLExpr expr;
}// 例如 select * from emp e inner join org o on e.org_id = o.id
// 其中left 'emp e' 是一个SQLExprTableSource,right 'org o'也是一个SQLExprTableSource
// condition 'e.org_id = o.id'是一个SQLBinaryOpExpr
class SQLJoinTableSource extends SQLTableSourceImpl {SQLTableSource left;SQLTableSource right;JoinType joinType; // INNER_JOIN/CROSS_JOIN/LEFT_OUTER_JOIN/RIGHT_OUTER_JOIN/...SQLExpr condition;
}// 例如 select * from (select * from temp) a,这里第一层from(...)是一个SQLSubqueryTableSource
SQLSubqueryTableSource extends SQLTableSourceImpl {SQLSelect select;
}

另外SQLExpr出现的地方也比较多,比如where语句,join条件,SQLSelectItem中等,因此,也需要细化了解一下,如

表达式实现类(SQLExpr)

**介绍:**SQLExpr 代表着 SQL 语句里的表达式,它是构成 SQL 查询的基础单元。这些表达式可以是常量、变量、函数调用、列引用等,能用来描述数据的筛选条件、计算逻辑等。

实际应用:实现 SQLExpr 接口的类就是用于表示和处理各种 SQL 表达式的类。SQLExpr出现的地方也比较多,比如where语句,join条件,SQLSelectItem中等,因此,也需要细化了解一下:

img

// SQLName是一种的SQLExpr的Expr,包括SQLIdentifierExpr、SQLPropertyExpr等
public interface SQLName extends SQLExpr {}// 例如 ID = 3 这里的ID是一个SQLIdentifierExpr
class SQLIdentifierExpr implements SQLExpr, SQLName {String name;
} // 例如 A.ID = 3 这里的A.ID是一个SQLPropertyExpr
class SQLPropertyExpr implements SQLExpr, SQLName {SQLExpr owner;String name;
} // 例如 ID = 3 这是一个SQLBinaryOpExpr
// left是ID (SQLIdentifierExpr)
// right是3 (SQLIntegerExpr)
class SQLBinaryOpExpr implements SQLExpr {SQLExpr left;SQLExpr right;SQLBinaryOperator operator;
}// 例如 select * from where id = ?,这里的?是一个SQLVariantRefExpr,name是'?'
class SQLVariantRefExpr extends SQLExprImpl { String name;
}// 例如 ID = 3 这里的3是一个SQLIntegerExpr
public class SQLIntegerExpr extends SQLNumericLiteralExpr implements SQLValuableExpr { Number number;// 所有实现了SQLValuableExpr接口的SQLExpr都可以直接调用这个方法求值@Overridepublic Object getValue() {return this.number;}
}// 例如 NAME = 'jobs' 这里的'jobs'是一个SQLCharExpr
public class SQLCharExpr extends SQLTextLiteralExpr implements SQLValuableExpr{String text;
}// SQLMethodInvokeExpr 是 SQLExpr 接口的实现类,专门用来解析和表示 SQL 中的函数调用
public class SQLMethodInvokeExpr extends SQLExprImpl {private String methodName;          // 方法名,如 "COUNT", "SUBSTR"private SQLExpr owner;              // 方法所有者(用于类似 o.method() 的调用)private List<SQLExpr> parameters;   // 参数列表private boolean distinct;           // 是否包含 DISTINCT 关键字private boolean trim;               // 是否 TRIM 函数特有属性// ... 其他属性和方法
}// 用于表示 SQL 聚合函数(Aggregate Function)。它继承自 SQLMethodInvokeExpr,专门处理聚合操作,如 COUNT(), SUM(), AVG(), MAX(), MIN() 等。
public class SQLAggregateExpr extends SQLMethodInvokeExpr implements Serializable, SQLReplaceable {

提示信息接口(SQLHint)

**介绍:**SQLHint 接口在 Druid 的 SQL 解析和处理体系中,代表着 SQL 提示信息。SQL 提示是一种给数据库查询优化器提供额外信息的方式,能够指导优化器以特定的方式执行查询,比如指定使用某个索引、调整查询的执行计划等。实现 SQLHint 接口的类就是用来表示和处理这些 SQL 提示信息的类。

img

查询实现接口(SQLSelectQuery)

**含义:**SQLSelectQuery 接口主要用于表示 SQL 中的 SELECT 查询语句。实现该接口的类承担着构建、解析和处理完整 SELECT 查询逻辑的任务。因此,称它们为 “SQL 查询语句类” 能突出其核心作用是代表完整的 SQL 查询语句。

**作用体现:**这些类可以包含查询的各个部分,如 SELECT 子句(指定要返回的列)、FROM 子句(指定数据源)、WHERE 子句(指定筛选条件)、GROUP BY 子句(分组)、ORDER BY 子句(排序)等。例如,一个实现类可能会将这些子句组合起来,形成一个完整的 SELECT 查询语句。

img

深入解析Statement(组成结构)

顶层抽象类

img

SelectStatement

SelectStatement实现类

所处的位置为:

img

class SQLSelectStatement implements SQLStatement {SQLSelect select;
}

SQLSelect

img

public class SQLSelect extends SQLObjectImpl implements SQLDbTypedObject {// 见上章节【可替换SQL组件类(SQLReplaceable接口实现)】protected SQLWithSubqueryClause withSubQuery;protected SQLOrderBy orderBy;protected SQLLimit limit;// 见章节【SQLSelectQuery】protected SQLSelectQuery query;protected List<SQLHint> hints;protected SQLObject restriction;protected boolean forBrowse;protected List<String> forXmlOptions;// 见章节 表达式实现类(SQLExpr)protected SQLExpr xmlPath;protected SQLExpr rowCount;protected SQLExpr offset;private SQLHint headHint;

SQLSelectQuery

img

SQLSelect包含一个SQLSelectQuery,都是组成的关系。

public class SQLSelect extends SQLObjectImpl implements SQLDbTypedObject {// sql中的子查询protected SQLWithSubqueryClause withSubQuery;// 主要select构成组成 【SQLSelectQueryBlock、SQLUnionQuery】protected SQLSelectQuery query;protected SQLOrderBy orderBy;protected SQLLimit limit;protected List<SQLHint> hints;protected SQLObject restriction;protected boolean forBrowse;protected List<String> forXmlOptions;protected SQLExpr xmlPath;protected SQLExpr rowCount;protected SQLExpr offset;private SQLHint headHint;

SQLSelectQuery有主要的两个派生类,分别是SQLSelectQueryBlock和SQLUnionQuery。

interface SQLSelectQuery extends SQLObject {}// 具体select查询条件其中组成
class SQLSelectQueryBlock implements SQLSelectQuery {// 字段内容List<SQLSelectItem> selectList;// 表来源 各种情况:SQLExprTableSource、SQLJoinTableSource、SQLSubqueryTableSourceSQLTableSource from;// where条件SQLExpr where;// group bySQLSelectGroupByClause groupBy;SQLOrderBy orderBy;SQLLimit limit;
}// 由SQLSelectQuery组成
class SQLUnionQuery implements SQLSelectQuery {SQLSelectQuery left;SQLSelectQuery right;SQLUnionOperator operator; // UNION/UNION_ALL/MINUS/INTERSECT
}

针对解析到的node树进行处理模板操作

关于不同结构之间血缘如何计算(简单记录)

// 单表 如:from table1  或者 from table1 as t1// 子查询表 如:from (select xxx, xxx from table 1)
CREATE TABLE new_table AS
select col3, col4 from (select col1 as col3, col2 as col4, col5 from table2
)as table3

img

// 场景3:join表 如:from table1 left|inner|right join table2 on table1.id = table2.id
CREATE TABLE new_table AS
SELECT col1 as cc1, col2 as cc2 FROM table1
union
-- left join
select col1, col2 from table4 as t4
left join table5 t4 on t1.col1 = t2.col1// 另一个例子
CREATE TABLE new_table AS
select col1, col2 from table4 as t4
left join table5 t4 on t1.col1 = t2.col1

img

img

复杂sql结合例子

复杂结合例子:

CREATE TABLEnew_table AS
SELECT col1 as cc1, col2 as cc2 FROM table1
union
selectcol3,col4
from(selectt4.col1 as col3,t5.col2 as col4fromtable4 as t4left join table5 t5 on t4.col3 = t5.col3) as tt

img

select类型:SqlSelectQueryBlock、SqlUnionQuery

tableSource类型:SQLSubqueryTableSource、SQLExprTableSource、SQLJoinTableSource

步骤一:确认目标表的字段

创建的表结构以第一组的sql为准:

CREATE TABLEnew_table AS
SELECT col1 as cc1, col2 as cc2 FROM table1
union
select

得到的一组目标写入字段为:

真实字段   别名字段    真实表名col1     cc1     new_tablecol2     cc2     new_table

步骤二:union右边的(带子查询、left join)

关注下面这部分:

union
selectcol3,col4
from(// 内部1️⃣selectt4.col1 as col3,t5.col2 as col4fromtable4 as t4left join table5 t5 on t4.col3 = t5.col3) as tt

以select开头作为一组,关注拿到的是该select 所拿到的selectItem字段:

如先看内部1️⃣:

selectt4.col1 as col3,t5.col2 as col4
fromtable4 as t4
left join table5 t5 on t4.col3 = t5.col3# 涉及到 join 关联
# 关于真实表名如何获取到:需要去单独去针对该selectsql匹配到表及别名预先获取到
# 1、首先为查询到select的字段:
# 关于select字段
真实字段  别名字段  真实表名
col1 			col3    t4
col2			col4    t5
# 关于from来源表为这种SQLExprTableSource情况,直接进行匹配
真实表名  表别名字段table4      t4table5      t5
# 2、借助select的字段去匹配from、left join的场景实现替换逻辑
# 由于t4与表别名字段t4匹配,则表字段记录的真实表名替换为相应的真实表名真实字段  别名字段  真实表名
col1 			col3    table4
col2			col4    table5

在看外部2️⃣,如下:

selectcol3 as col5,col4 as col6
from(// 内部1️⃣已替换真实字段  别名字段  真实表名col1 			col3    table4col2			col4    table5    ) as tt

此时select的字段为col3、col4

逻辑应该是拿其col3匹配1️⃣中的中的字段(先别名、无别名再真实字段),匹配得到了则取相应table名字。

tt的col3匹配到1️⃣如下:

真实字段  别名字段  真实表名
col1 			col3    table4// 中间过程
// 1、tt的真实字段col3尝试匹配内部1️⃣中的别名
//      1.1、匹配成功一致
//          1.1.1、判断tt自身这个字段是否有别名,有的话则将col5填充别名字段到表中
//          1.1.2、如果tt自身没有别名,则不动
//      1.2、匹配失败:则该字段舍弃,抛出异常记录(sql有问题了)
// 按照规则会走到1.1.1替换后如下得到一组字段记录真实字段  别名字段  真实表名
col1 			col5    table4

tt的col4匹配1️⃣如下:

真实字段  别名字段  真实表名
col2			col4    table5  // 中间过程如上所述
// 同样按照规则走的是1.1.1 替换后如下真实字段  别名字段  真实表名
col2			col6    table5

最终外部2转换得到的一组字段为:

真实字段  别名字段  真实表名
col1 			col5    table4
col2			col6    table5

步骤三:union左边的

SELECT col1 as cc1, col2 as cc2 FROM table1
union

参考步骤二:

真实字段  别名字段  真实表名
col1 			cc1    table1
col2			cc2    table1

合并去看

目标表:

真实字段  别名字段      表名col1    cc1     new_tablecol2    cc2     new_table

union左:

真实字段  别名字段  真实表名
col1 			cc1    table1
col2			cc2    table1

union右:

真实字段  别名字段  真实表名
col1 			col5    table4
col2			col6    table5

血缘去匹配过程为:

union有几个就匹配几次,这里有两个就匹配两次 组成两组血缘

注意注意!!! 该过程与步骤2、3的追溯过程不一样

第一组:目标字段 & union左

# 目标字段
真实字段   别名字段   真实表名col1    cc1     new_tablecol2    cc2     new_table# union左sql字段
真实字段  别名字段  真实表名
col1 			cc1    table1
col2			cc2    table1

匹配过程:

1、应该是一一对应的过程,而不是追溯寻找的过程。

2、例如【目标字段】第一行对应下面【union左sql字段】第一行,【目标字段】第二行对应下面【union左sql字段】第二行,各自只取真实的即可

img

来源表  来源字段   目标表      目标字段
table1  col1   new_table     cc1
table1  col2   new_table     cc2

3、关于select查询表有join连接逻辑(也要产生血缘)

# 目标字段
真实字段   别名字段   真实表名col1    cc1     new_tablecol2    cc2     new_table# 涉及到 join 关联
真实字段  真实表名(需要去匹配到)  表别名字段
col3     table4                t4
col3     table5                t5

产生4条血缘规则(每一个目标真实表及字段映射join关联的真实表及字段):

来源表  来源字段   目标表      目标字段
table4  col3   new_table     cc1
table5  col3   new_table     cc2

第二组:目标血缘 & union右

datablau血缘问题记录

问题1:复杂sql加上on条件之后血缘解析有问题

无问题:

CREATE TABLEnew_table AS
SELECT col1 as cc1, col2 as cc2 FROM table1
union
selectcol3,col4
from(selectt4.col1 as col3,t5.col2 as col4fromtable4 as t4left join table5 t5 on t4.col1 = t5.col1) as tt

img

有问题:修改on条件之后,影响new_table中的字段为col3、col3

// left join table5 t5 on t4.col1 = t5.col1
CREATE TABLEnew_table AS
SELECT col1 as cc1, col2 as cc2 FROM table1
union
selectcol3,col4
from(selectt4.col1 as col3,t5.col2 as col4fromtable4 as t4left join table5 t5 on t4.col3 = t5.col3) as tt

img

问题2:union on问题

CREATE TABLE new_table AS
SELECT col1, col2 FROM table1
UNION
SELECT col3, col4 FROM table2;

img

问题描述:错误的将目标表中的字段作为了col3、col4。

资料获取

大家点赞、收藏、关注、评论啦~

精彩专栏推荐订阅:在下方专栏👇🏻

  • 长路-文章目录汇总(算法、后端Java、前端、运维技术导航):博主所有博客导航索引汇总
  • 开源项目Studio-Vue—校园工作室管理系统(含前后台,SpringBoot+Vue):博主个人独立项目,包含详细部署上线视频,已开源
  • 学习与生活-专栏:可以了解博主的学习历程
  • 算法专栏:算法收录

更多博客与资料可查看👇🏻获取联系方式👇🏻,🍅文末获取开发资源及更多资源博客获取🍅

http://www.xdnf.cn/news/1243549.html

相关文章:

  • Java开发时出现的问题---语言特性与基础机制陷阱
  • STM32_Hal库学习SPI
  • 15个命令上手Linux!
  • Redis之通用命令与String类型存储
  • javacc实现简单SQL解析器
  • 【云馨AI-大模型】2025年8月第一周AI浪潮席卷全球:创新与政策双轮驱动
  • VPS云服务器Linux系统备份策略与灾难恢复方案设计
  • SQL基础语法
  • Qt按键响应
  • 倒排索引:Elasticsearch 搜索背后的底层原理
  • 【C语言】自定义类型:联合体与枚举
  • SpringMVC在前后端分离架构中的执行流程详解
  • 句子表征-文本匹配--representation-based/interactive-based
  • MS-DOS 常用指令集
  • 机器学习——学习路线
  • 2.Java和C++有什么区别
  • Demo-LangGraph构建Agent
  • 【Spring】SpringBoot 自动配置,@ComponentScan、@Import、ImportSelector接口
  • LeetCode 132:分割回文串 II
  • Linux开发利器:探秘开源,构建高效——基础开发工具指南(下)【make/Makefile】
  • 水面垃圾清扫船cad【6张】三维图+设计说明书
  • Jmeter进行性能并发测试
  • 【Java】使用FreeMarker来实现Word自定义导出
  • C++高频知识点(十四)
  • 京东商品详情API技术文档框架及Python实现方案
  • sqli-labs:Less-27a关卡详细解析
  • 《Python 实用项目与工具制作指南》· 2.3 导入
  • Bean的生命周期和循环依赖问题的解决
  • curl发送文件bodyParser无法获取请求体的问题分析
  • 嵌入式硬件中三极管推挽电路控制与实现