天画项目-低代码平台-er图生成设计与实现

博客围绕基于已有表结构自动生成e-r图展开。背景是项目架构师用plantUML构建表结构e-r图,作者业余项目也有此需求。需求包括通过Java api构建plantUML文件、安装渲染插件、自动识别表间关系生成e-r图。设计方案基于mysql表元数据读取数据生成文件,并给出多种实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、背景&需求

1.1 背景

我在公司负责的一个交易核心项目中,项目架构师使用了plantUML进行了表结构e-r图的构建。我非常好奇,因为之前画e-r图都是使用别的软件进行构建的。另外一方面我的业余项目中也需要构建e-r图,但是我不想为每个项目模块去花时间手动构建e-r图。

1.2 需求

  1. 基于已有表结构通过Java api构建plantUML文件
  2. idea安装支持plantUML文件渲染的插件
  3. 自动识别表间关系,生成e-r图

二、设计方案&实现

2.1 方案描述

基于mysql表元数据的方式读取数据然后生成plantUML格式的文件

2.2 方案实现

2.2.1 定义服务bean模型&接口

我们先看下plantUML的e-r图大概是长啥样的,同时可以基于此做数据模型的适配和集成。如下图是一个简单的数据库表描述:

WX20201221-144730.png

左边是文件内容,右边则是e-r效果图。这里的服务bean模型则是对数据库元数据信息进行的抽象,由于之前已经对数据库元数据做了解析,所以可以把对应的模型代码直接拿过来,做一些属性上的裁剪,代码如下:

/**
 * Created on 2018-7-19.
 *
 * @author: coderman
 * @version: V1.0
 * @Desc: 表字段描述
 */
public class ColumnBean {
    /**
     * 表名
     */
    private String tableName;
    /**
     * 字段名
     */
    private String columnName;

    /**
     * 数据类型
     */
    private String dataType;

    /**
     * 是否是主键(或者其他键)
     */
    private String columnKey;

    /**
     * 字段类型
     */
    private String columnType;

    /**
     * 字段描述
     */
    private String columnComment;
    get....
    set....
 }
/**
 * Created  on 2018-7-19.
 *
 * @author: fanchunshuai
 * @version: V1.0
 * @Desc:表信息描述
 */
public class TableBean {
    /**
     * 表名
     */
    private String tableName;
    /**
     * 表描述
     */
    private String tableComment;

    /**
     * 表字段简单模型
     */
    private List<ColumnBean> columnBeanList;
    get....
    set....
 }

2.2.2 jar包形式服务

这个功能原则上不依赖任何第三方数据源和第三方框架,也不需要链接具体的数据源做解析,因此可以以jar包的形式提供服务,并通过接口的方式对外暴露能力。这里定义一个接口:

/**
 * Description: 生成e-r图的服务接口
 * date: 2020/10/20
 *
 * @author fanchunshuai
 * @version 1.0.0
 * @since JDK 1.8
 */
public interface ErPictureService {
    /**
     * 生成执行入口
     * @param tableBeanList
     * @return
     */
    public boolean generateER(String path, List<TableBean> tableBeanList);
}

具体实现还需要文件读写,进行生成对应的plantuml文件。

2.2.3 单独使用

将代码clone下来之后看main方法的案例,这里贴一下单独使用的源码case:
 

public class App 
{
    private static TableBean getStudentBean(){
        TableBean tableBean = new TableBean();
        tableBean.setTableComment("学生表");
        tableBean.setTableName("student");

        List<ColumnBean> columnBeanList = new ArrayList<>();
        ColumnBean columnBean = new ColumnBean();
        columnBean.setColumnComment("主键");
        columnBean.setColumnName("id");
        columnBean.setTableName("student");
        columnBean.setColumnType("bigint");
        ColumnBean columnBean2 = new ColumnBean();
        columnBean2.setColumnComment("姓名");
        columnBean2.setColumnName("chinese_name");
        columnBean2.setTableName("student");
        columnBean2.setColumnType("varchar");

        ColumnBean columnBean3 = new ColumnBean();
        columnBean3.setColumnComment("年龄");
        columnBean3.setColumnName("age");
        columnBean3.setTableName("student");
        columnBean3.setColumnType("int");

        ColumnBean columnBean4 = new ColumnBean();
        columnBean4.setColumnComment("所属班级");
        columnBean4.setColumnName("class_id");
        columnBean4.setTableName("student");
        columnBean4.setColumnType("bigint");

        columnBeanList.add(columnBean);
        columnBeanList.add(columnBean2);
        columnBeanList.add(columnBean3);
        columnBeanList.add(columnBean4);

        tableBean.setColumnBeanList(columnBeanList);
        return tableBean;
    }

    private static TableBean getCourseBean(){
        TableBean tableBean = new TableBean();
        tableBean.setTableComment("课程表");
        tableBean.setTableName("course");

        List<ColumnBean> columnBeanList = new ArrayList<>();
        ColumnBean columnBean = new ColumnBean();
        columnBean.setColumnComment("主键");
        columnBean.setColumnName("id");
        columnBean.setTableName("course");
        columnBean.setColumnType("bigint");
        ColumnBean columnBean2 = new ColumnBean();
        columnBean2.setColumnComment("课程名称");
        columnBean2.setColumnName("course_name");
        columnBean2.setTableName("course");
        columnBean2.setColumnType("varchar");

        ColumnBean columnBean3 = new ColumnBean();
        columnBean3.setColumnComment("上课老师");
        columnBean3.setColumnName("teacher_id");
        columnBean3.setTableName("course");
        columnBean3.setColumnType("bigint");

        ColumnBean columnBean4 = new ColumnBean();
        columnBean4.setColumnComment("课时数");
        columnBean4.setColumnName("course_num");
        columnBean4.setTableName("course");
        columnBean4.setColumnType("int");

        columnBeanList.add(columnBean);
        columnBeanList.add(columnBean2);
        columnBeanList.add(columnBean3);
        columnBeanList.add(columnBean4);

        tableBean.setColumnBeanList(columnBeanList);
        return tableBean;
    }

    private static TableBean getScoreBean(){
        TableBean tableBean = new TableBean();
        tableBean.setTableComment("分数表");
        tableBean.setTableName("score");

        List<ColumnBean> columnBeanList = new ArrayList<>();
        ColumnBean columnBean = new ColumnBean();
        columnBean.setColumnComment("主键");
        columnBean.setColumnName("id");
        columnBean.setTableName("score");
        columnBean.setColumnType("bigint");
        ColumnBean columnBean2 = new ColumnBean();
        columnBean2.setColumnComment("课程Id");
        columnBean2.setColumnName("course_id");
        columnBean2.setTableName("score");
        columnBean2.setColumnType("bigint");

        ColumnBean columnBean3 = new ColumnBean();
        columnBean3.setColumnComment("学生ID");
        columnBean3.setColumnName("student_id");
        columnBean3.setTableName("score");
        columnBean3.setColumnType("bigint");

        ColumnBean columnBean4 = new ColumnBean();
        columnBean4.setColumnComment("分数");
        columnBean4.setColumnName("score_num");
        columnBean4.setTableName("score");
        columnBean4.setColumnType("int");

        columnBeanList.add(columnBean);
        columnBeanList.add(columnBean2);
        columnBeanList.add(columnBean3);
        columnBeanList.add(columnBean4);

        tableBean.setColumnBeanList(columnBeanList);
        return tableBean;
    }

    private static TableBean getClassBean(){
        TableBean tableBean = new TableBean();
        tableBean.setTableComment("班级表");
        tableBean.setTableName("class");

        List<ColumnBean> columnBeanList = new ArrayList<>();
        ColumnBean columnBean = new ColumnBean();
        columnBean.setColumnComment("主键");
        columnBean.setColumnName("id");
        columnBean.setTableName("class");
        columnBean.setColumnType("bigint");
        ColumnBean columnBean2 = new ColumnBean();
        columnBean2.setColumnComment("班级名称");
        columnBean2.setColumnName("class_name");
        columnBean2.setTableName("class");
        columnBean2.setColumnType("varchar");

        ColumnBean columnBean3 = new ColumnBean();
        columnBean3.setColumnComment("班级序号");
        columnBean3.setColumnName("class_num");
        columnBean3.setTableName("class");
        columnBean3.setColumnType("int");

        ColumnBean columnBean4 = new ColumnBean();
        columnBean4.setColumnComment("学生数");
        columnBean4.setColumnName("student_num");
        columnBean4.setTableName("class");
        columnBean4.setColumnType("int");

        columnBeanList.add(columnBean);
        columnBeanList.add(columnBean2);
        columnBeanList.add(columnBean3);
        columnBeanList.add(columnBean4);

        tableBean.setColumnBeanList(columnBeanList);
        return tableBean;
    }

    private static TableBean getTeacherClassBean(){
        TableBean tableBean = new TableBean();
        tableBean.setTableComment("老师表");
        tableBean.setTableName("teacher");

        List<ColumnBean> columnBeanList = new ArrayList<>();
        ColumnBean columnBean = new ColumnBean();
        columnBean.setColumnComment("主键");
        columnBean.setColumnName("id");
        columnBean.setTableName("teacher");
        columnBean.setColumnType("bigint");
        ColumnBean columnBean2 = new ColumnBean();
        columnBean2.setColumnComment("教师姓名");
        columnBean2.setColumnName("teacher_name");
        columnBean2.setTableName("teacher");
        columnBean2.setColumnType("varchar");

        ColumnBean columnBean3 = new ColumnBean();
        columnBean3.setColumnComment("教师年龄");
        columnBean3.setColumnName("age");
        columnBean3.setTableName("teacher");
        columnBean3.setColumnType("int");

        ColumnBean columnBean4 = new ColumnBean();
        columnBean4.setColumnComment("教师职称");
        columnBean4.setColumnName("post_name");
        columnBean4.setTableName("teacher");
        columnBean4.setColumnType("varchar");

        columnBeanList.add(columnBean);
        columnBeanList.add(columnBean2);
        columnBeanList.add(columnBean3);
        columnBeanList.add(columnBean4);

        tableBean.setColumnBeanList(columnBeanList);
        return tableBean;
    }


    public static void main( String[] args )
    {
        List<TableBean> tableBeanList = new ArrayList<>();
        tableBeanList.add(getClassBean());
        tableBeanList.add(getCourseBean());
        tableBeanList.add(getScoreBean());
        tableBeanList.add(getStudentBean());
        tableBeanList.add(getTeacherClassBean());
        ErPictureService erPictureService = new ErPictureServiceImpl();
        String path = "/Users/myspace/tianhua/tianhua-dberGenerator/src/main/resources/teachSystem.puml";
        path = "E:\\workspace\\newWorkspace\\codeMaker\\codemaker-dberPicture\\src\\main\\resources\\teachSystem.puml";
        erPictureService.generateER(path,tableBeanList);
    }

运行上述代码可看到下面的效果图:

WX20201221-145619@2x.png

2.2.4 api形式提供服务

目前这个功能已经集成到codeMaker-core中了,可以启动服务访问接口/getproject/erpicture则可以看到效果。

2.2.5 spring 集成提供服务

由于是基于接口实现,本工具类并没有集成spring框架,但是可以将接口ErPictureService注册为spring bean.

 

我最近整了一个公众号,持续输出原创内容,敬请关注:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值