女朋友说没找到好用的画ER图工具,于是我们自己手搓了一个!
点击关注公众号,“技术干货”及时达!
前言学校数据库作业是让我们画一堆ER图,我对象把这个重任交给了我。起初我一听画er图嘛,那不简简单单,我没有很在意。直到我尝试花了一个之后,就已经过了一个上午,看着剩下的一堆和仅剩2天的时间,我天塌了.........女朋友觉得我不在意她,于是她和我说写不完了就分手吧!
于是我躺在床上幻想我们曾经的点点滴滴.............灵机一动那我们两个人写一个不就好了嘛,不仅可以增加我们团队开发项目的经验,还可以提高我们学习效率。沟通好之后直接开干!经历几天时间终于把工具做好啦,大家可以试试在线生成ER图工具
项目介绍在数据库课程或毕业设计阶段,手绘ER图是否总是让你头疼不已?无论是老师布置的数据库设计任务,还是毕业项目中的系统建模,ER图似乎成了必经的难关。但你是否意识到,ER图其实与数据库中的表结构一一对应,难道我们真的需要一个个手动绘制表、字段和关系吗?答案是否定的!
为了解决这一难题,我们特别开发了一款 「在线SQL转ER图工具」。只需将你的SQL语句输入工具,它便能自动解析数据库表结构,精准生成专业的ER图。无论是创建表、设置外键约束,还是处理其他复杂的数据库结构,这款工具都能一键完成转化,让你轻松拥有清晰、规范的ER图。
我们深知,学校的数据库课程通常要求根据特定需求设计数据库并绘制ER图,许多同学因此苦恼不已。手动画图不仅耗时费力,还容易出错。我们开发这个工具,正是为了解决这一痛点。它不仅帮助你快速、准确地生成ER图,还让你能够专注于数据库设计的核心内容,而无需花费大量时间在绘图上。
无论你是数据库初学者,还是已经具备一定基础的学生,这款工具都能大幅提升你的工作效率,助你在数据库作业和毕业设计中游刃有余,轻松应对各种挑战。
立即试用我们的 「画ER图工具」,告别繁琐手绘,节省宝贵时间,让你的数据库设计更加高效与专业!
工具地址:在线SQL转ER图工具有了这款工具,你将享受以下超赞优势:
「极速生成ER图」:只需复制粘贴你的SQL语句到工具里,立马自动解析生成ER图,省下大量手动绘图的时间。「智能识别表结构和关系」:工具能精准识别SQL里的表定义、字段类型、约束条件等信息,自动生成关系图,细节一点不错过。「易于理解和修改」:ER图清晰展示了各数据表之间的关系,帮你更好地掌握数据库结构。图形直观明了,修改起来也超级方便。「轻松搞定作业难题」:不再为学校要求手动绘制ER图而头疼,有了这个工具,画图变得简单轻松,再也不用为此烦心啦!在线SQL转ER图SQL直接转ER图功能:
例如我现在有一段SQL语句,我想要把它转化为作业中需要用到的ER图效果:
-- 学生信息表CREATE TABLE `students`( `id` INT AUTO_INCREMENT COMMENT '学生ID', `name` VARCHAR(50) NOT NULL COMMENT '姓名', `gender` ENUM ('男', '女') NOT NULL COMMENT '性别', `birth_date` DATE COMMENT '出生日期', `email` VARCHAR(100) COMMENT '邮箱', `phone` VARCHAR(20) COMMENT '电话', `address` VARCHAR(255) COMMENT '家庭地址', `enrollment_date` DATE COMMENT '入学日期', `status` ENUM ('在校', '毕业', '退学') DEFAULT '在校' COMMENT '状态', PRIMARY KEY (`id`)) COMMENT ='学生';
-- 课程信息表CREATE TABLE `courses`( `id` INT AUTO_INCREMENT COMMENT '课程ID', `name` VARCHAR(100) NOT NULL COMMENT '课程名称', `description` TEXT COMMENT '课程描述', `credit` INT NOT NULL COMMENT '学分', `teacher` VARCHAR(50) COMMENT '授课教师', PRIMARY KEY (`id`)) COMMENT ='课程';
-- 学生选课记录表CREATE TABLE `student_courses`( `id` INT AUTO_INCREMENT COMMENT '选课记录ID', `student_id` INT NOT NULL COMMENT '学生ID', `course_id` INT NOT NULL COMMENT '课程ID', `enroll_date` DATE COMMENT '选课日期', `grade` DECIMAL(5, 2) COMMENT '成绩', PRIMARY KEY (`id`), FOREIGN KEY (`student_id`) REFERENCES `students` (`id`) ON DELETE CASCADE, FOREIGN KEY (`course_id`) REFERENCES `courses` (`id`) ON DELETE CASCADE) COMMENT ='选课记录';使用步骤如下:
将你的SQL复制到输入框中选择使用SQL转为ER图的模式点击生成按钮即可生成对应的ER图
如果对图中的节点不满意,可以对对应的节点名称进行修改,操作步骤如下:
点击对应的节点点击右上角的节点详情修改对应的实体名称可以查看实体对应的属性,如果需要修改对应的节点属性,请点击对应的节点再去修改实体名称可以在右下角给实体新增属性,属性一般使用椭圆来表示
AI智能生成ER图系统提供根据自然语言来直接生成对应的系统ER图功能,可用于快速完成作业,或者在没有SQL的情况下使用
在输入框输入对应的系统名称(只需要你想要生成的系统,例如学生管理系统)在生成模式中选择使用AI来生成点击生成按钮即可生成对应的系统ER图
技术相关(部分后端技术)1. 解析sql语句解析步骤「预处理SQL语句」:去除SQL语句中的注释和空白字符。「关键字检查」:对SQL语句中的关键字进行过滤。「SQL语句解析」:使用SQL解析工具解析SQL语句,提取语法树或抽象语法树(AST)。「遍历解析结果」:提取表名、字段信息以及字段注释。「输出或存储解析结果」:最终将提取的表结构信息输出或存储。1. 预处理SQL语句在解析SQL语句之前,过滤SQL中的注释信息,避免它们干扰解析。常见的注释类型包括:
单行注释(-- 或 #)多行注释(/*...*/)使用正则表达式将注释部分去除:
String cleanSql = sql .replace("\r\n\t", " ") // 去除单行注释以 "--" 开头 .replaceAll("--.*?(\r\n|\n)", "") // 去除单行注释以 "#" 开头 .replaceAll("#.*?(\r\n|\n)", "") // 去除多行注释以 "/*...*/" 包含 .replaceAll("/\*.*?\*/", "");2. 关键字检查(不重要)在进行SQL解析之前,过滤SQL中的潜在风险,
SqlUtil.filterKeyword(cleanSql);
该方法会遍历SQL语句,检查是否包含在SQL_REGEX中定义的关键字。特殊情况如CREATE TABLE语句会被跳过检查,防止误报。其实这里不用处理,因为没有涉及对本地数据库的操作
3. SQL语句解析使用SQL解析工具(如SQLUtils.parseStatements)将清理后的SQL语句解析为抽象语法树(AST)。在这里,我们使用了MySqlCreateTableStatement来解析CREATE TABLE语句:
ListSQLStatement sqlStatements = SQLUtils.parseStatements(cleanSql, DbType.mysql);解析后的SQL语句被转化为一系列的SQLStatement对象,其中包括各种类型的SQL语句(如CREATE TABLE、INSERT等)。
4. 遍历解析结果得到SQL语句的抽象语法树后,接下来就是遍历树中的节点,提取所需的信息。我们首先检查当前的SQLStatement是否是CREATE TABLE语句,然后提取表名和字段信息。
for (SQLStatement sqlStatement : sqlStatements) { if (sqlStatement instanceof MySqlCreateTableStatement) { MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement;
// 提取表名并去除反引号 String tableName = createTableStatement.getTableName().replaceAll("`", ""); System.out.println("Table Name: " + tableName);
// 提取表注释 String tableComment = tableName; // 默认使用表名 if (createTableStatement.getComment() != null) { SQLExpr commentExpr = createTableStatement.getComment(); if (commentExpr instanceof SQLCharExpr) { tableComment = ((SQLCharExpr) commentExpr).getText().replaceAll("`", ""); } } System.out.println("Table Comment: " + tableComment);这段代码提取了表名和表注释。若没有指定注释,默认使用表名作为注释。
5. 提取字段信息在CREATE TABLE语句中,字段信息通常以SQLColumnDefinition的形式出现,我们通过遍历表中的元素列表来提取每个字段的名称和注释。
for (SQLTableElement element : createTableStatement.getTableElementList()) { if (element instanceof SQLColumnDefinition) { SQLColumnDefinition columnDefinition = (SQLColumnDefinition) element;
// 提取字段名并去除反引号 String columnName = columnDefinition.getColumnName().replaceAll("`", "");
// 提取字段注释 String columnComment = columnDefinition.getColumnName(); // 默认使用字段名称作为注释 SQLExpr commentExpr = columnDefinition.getComment(); if (commentExpr instanceof SQLCharExpr) { columnComment = ((SQLCharExpr) commentExpr).getText().replaceAll("`", ""); }
// 输出字段信息 System.out.println(" Column Name: " + columnName); System.out.println(" Column Comment: " + columnComment); }}「完整代码:」「工具类」
/** * sql操作工具类 * * */ public class SqlUtil { /** * 定义常用的 sql关键字 */ public static String SQL_REGEX = "and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
/** * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) */ public static String SQL_PATTERN = "[a-zA-Z0-9_\ \,\.]+";
/** * 限制orderBy最大长度 */ private static final int ORDER_BY_MAX_LENGTH = 500;
/** * SQL关键字检查 */ public static void filterKeyword(String value) { if (StringUtils.isEmpty(value)) { return; } String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\|"); for (String sqlKeyword : sqlKeywords) { // 更新:跳过 CREATE TABLE 语句的检查 if (StringUtils.startsWithIgnoreCase(value.trim(), "CREATE TABLE")) { continue; // 如果是 CREATE TABLE 语句,跳过过滤检查 } if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) -1) { throw new UtilException("参数存在SQL注入风险"); } } }
/** * 检查字符,防止注入绕过 */ public static String escapeOrderBySql(String value) { if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) { throw new UtilException("参数不符合规范,不能进行查询"); } if (StringUtils.length(value) ORDER_BY_MAX_LENGTH) { throw new UtilException("参数已超过最大限制,不能进行查询"); } return value; }
/** * 验证 order by 语法是否符合规范 */ public static boolean isValidOrderBySql(String value) { return value.matches(SQL_PATTERN); }
} public static void parseMultipleSql(String sql) { // 去除SQL中的注释 String cleanSql = sql .replace("\r\n\t", " ") // 去除单行注释以 "--" 开头 .replaceAll("--.*?(\r\n|\n)", "") // 去除单行注释以 "#" 开头 .replaceAll("#.*?(\r\n|\n)", "") // 去除多行注释以 "/*...*/" 包含 .replaceAll("/\*.*?\*/", "");
// 过滤SQL关键字并解析SQL语句 SqlUtil.filterKeyword(cleanSql); ListSQLStatement sqlStatements = SQLUtils.parseStatements(cleanSql, DbType.mysql);
// 遍历所有SQL语句 for (SQLStatement sqlStatement : sqlStatements) { // 检查是否为创建表语句 if (sqlStatement instanceof MySqlCreateTableStatement) { MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement;
// 提取表名并去除反引号 String tableName = createTableStatement.getTableName().replaceAll("`", "");
// 提取表注释,若没有注释则使用表名作为默认值 String tableComment = tableName; // 默认使用表名 if (createTableStatement.getComment() != null) { SQLExpr commentExpr = createTableStatement.getComment(); if (commentExpr instanceof SQLCharExpr) { tableComment = ((SQLCharExpr) commentExpr).getText().replaceAll("`", ""); } }
// 输出表信息 System.out.println("Table Name: " + tableName); System.out.println("Table Comment: " + tableComment);
// 遍历表的字段信息 for (SQLTableElement element : createTableStatement.getTableElementList()) { // 检查是否为字段定义 if (element instanceof SQLColumnDefinition) { SQLColumnDefinition columnDefinition = (SQLColumnDefinition) element;
// 提取字段名并去除反引号 String columnName = columnDefinition.getColumnName().replaceAll("`", "");
// 提取字段注释,若没有注释则使用字段名称作为默认值 String columnComment = columnDefinition.getColumnName(); // 默认使用字段名称作为注释 SQLExpr commentExpr = columnDefinition.getComment(); if (commentExpr instanceof SQLCharExpr) { // 如果字段有注释,则提取注释内容 columnComment = ((SQLCharExpr) commentExpr).getText().replaceAll("`", ""); }
// 输出字段信息 System.out.println(" Column Name: " + columnName); System.out.println(" Column Comment: " + columnComment); } } } } }
「测试:」
public static void main(String[] args) throws Exception { String sql = "CREATE TABLE Employee\n" + "(\n" + " id INT PRIMARY KEY comment '主键ID',\n" + " name VARCHAR(100) comment '姓名'\n" + ") comment '员工';\n" + "CREATE TABLE Department\n" + "(\n" + " id INT PRIMARY KEY comment '主键ID',\n" + " name VARCHAR(100) comment '部门名称',\n" + " location VARCHAR(100) comment '位置'\n" + ") comment '部门';\n" +
"CREATE TABLE Project\n" + "(\n" + " id INT PRIMARY KEY comment '主键ID',\n" + " name VARCHAR(100) comment '项目名称',\n" + " budget DECIMAL(10, 2) comment '预算'\n" + ") comment '项目';\n" + "CREATE TABLE Employee_Project\n" + "(\n" + " employee_id INT comment '员工ID',\n" + " project_id INT comment '项目ID',\n" + " hours INT comment '工作小时数',\n" + " PRIMARY KEY (employee_id, project_id)\n" + ") comment '员工-项目关系表';\n";
parseMultipleSql(sql); }
「运行结果:」
采用设计模式ER图转换工具的设计策略模式(Strategy Pattern)「概述」策略模式定义了一系列算法,将每个算法封装起来,并使它们可以互换。这种模式使得算法可以独立于使用它的客户端变化。
接口定义
ErConversionStrategy
接口定义了生成ER图的基本方法。
public interface ErConversionStrategy { GraphData generateErDiagram(String userInput) throws Exception; int key();}具体策略实现
@Componentpublic class SqlErConversionStrategy implements ErConversionStrategy { /* ... */ }
@Componentpublic class AiErConversionStrategy implements ErConversionStrategy { /* ... */ }SqlErConversionStrategy:基于SQL解析生成ER图。AiErConversionStrategy:利用AI模型生成ER图。「优点」「灵活性高」:可以根据不同的业务需求(如不同的转换方式)轻松切换策略。「易于扩展」:添加新的转换策略,只需实现 ErConversionStrategy 接口,无需修改现有代码。「单一职责」:每个策略类只负责一种转换方式,职责清晰。「实现流程」「定义接口」:ErConversionStrategy 定义了生成ER图的方法。「实现具体策略」:不同的转换方式(如SQL解析、AI生成)通过实现该接口来提供具体的实现。「客户端使用」:ErController 通过工厂获取具体的策略并调用其方法生成ER图。工厂模式(Factory Pattern)「概述」工厂模式定义了一个用于创建对象的接口,让子类决定实例化哪一个类。工厂模式使一个类的实例化延迟到其子类。
工厂类
ErConversionStrategyFactory
负责管理和提供不同的
ErConversionStrategy
实例。
@Componentpublic class ErConversionStrategyFactory implements InitializingBean { @Resource private ApplicationContext context;
private static final MapInteger, ErConversionStrategy strategies = new HashMap();
public static ErConversionStrategy getStrategy(int businessType) { ErConversionStrategy sqlStrategy = strategies.get(businessType); ThrowUtils.throwIf(sqlStrategy == null, new BusinessException("业务类型无效")); return sqlStrategy; }
@Override public void afterPropertiesSet() throws Exception { MapString, ErConversionStrategy beansOfType = context.getBeansOfType(ErConversionStrategy.class); beansOfType.values().forEach(strategy - strategies.put(strategy.key(), strategy)); }}「获取策略」:通过 getStrategy 方法,根据业务类型(businessType)获取相应的策略实例。
「优点」「集中管理」:所有策略实例的创建和管理集中在工厂类中,便于维护和扩展。「解耦」:客户端(如 ErController)无需知道具体的策略实现,只需通过工厂获取所需策略,降低了耦合度。「灵活性」:可以根据需要动态添加或移除策略,而不影响客户端代码。「实现流程」「初始化」:ErConversionStrategyFactory 在 Spring 容器初始化时,通过 afterPropertiesSet 方法获取所有 ErConversionStrategy 的实现并存入 strategies 映射。「获取策略」:客户端通过调用 getStrategy 方法,传入业务类型,工厂返回相应的策略实例。「使用策略」:客户端使用获取到的策略实例执行具体的转换逻辑。依赖注入(Dependency Injection)「概述」依赖注入是一种设计模式,通过将对象的依赖关系从代码中移除,转而通过外部注入,使得系统更加模块化、易于测试和维护。
Spring 框架:使用 @Component,@Resource等注解实现依赖注入。
@Componentpublic class ErConversionStrategyFactory implements InitializingBean { @Resource private ApplicationContext context; // ...}
@Componentpublic class AiErConversionStrategy implements ErConversionStrategy { @Resource private ChatLanguageModel chatLanguageModel; @Resource private CurrencyService currencyService; // ...}
@RestController@RequestMapping("/er")public class ErController { @Resource private ChatLanguageModel chatLanguageModel; // ...}「优点」「松耦合」:对象之间的依赖关系由框架管理,减少了类之间的直接依赖。「易于测试」:可以方便地替换依赖对象为mock对象,进行单元测试。「模块化」:各个模块之间通过接口和依赖注入进行通信,增强了系统的模块化程度。单例模式(Singleton Pattern)「概述」单例模式确保一个类只有一个实例,并提供一个全局访问点。
Spring Bean
ErConversionStrategyFactory
作为 Spring 容器中的单例 Bean,确保在整个应用中只有一个工厂实例。
@Componentpublic class ErConversionStrategyFactory implements InitializingBean { // ...}「优点」「节省资源」:避免了多次创建工厂实例,节省内存和资源。「全局访问」:通过工厂的静态方法 getStrategy,可以在任何地方方便地获取策略实例。通过结合策略模式和工厂模式,ER图转换工具实现了高度的灵活性和可扩展性。依赖注入和单例模式进一步提升了系统的模块化和资源管理效率。这种设计不仅符合面向对象设计原则(如开闭原则和单一职责原则),如果女朋友嫌弃功能不齐全不够好可以直接加呢!这样不会破坏其他的部分,还为可以避免未来吵架呢,哈哈哈。
「整体架构流程」「用户请求」:ErController 接收到生成ER图的请求,包含SQL语句和业务类型。「获取策略」:ErController 通过 ErConversionStrategyFactory 根据业务类型获取相应的策略实例。「执行转换」:策略实例(如 SqlErConversionStrategy 或 AiErConversionStrategy)执行 generateErDiagram 方法,生成ER图数据。「返回结果」:ErController 将生成的ER图数据封装到 BaseResponse 中返回给客户端。最后由于时间关系这里先介绍这么多,产品还在继续开发中。如有问题希望大家积极反馈哦!!!
点击关注公众号,“技术干货”及时达!
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线