一、背景&需求
1.1 背景
我在公司负责的一个交易核心项目中,项目架构师使用了plantUML进行了表结构e-r图的构建。我非常好奇,因为之前画e-r图都是使用别的软件进行构建的。另外一方面我的业余项目中也需要构建e-r图,但是我不想为每个项目模块去花时间手动构建e-r图。
1.2 需求
- 基于已有表结构通过Java api构建plantUML文件
- idea安装支持plantUML文件渲染的插件
- 自动识别表间关系,生成e-r图
二、设计方案&实现
2.1 方案描述
基于mysql表元数据的方式读取数据然后生成plantUML格式的文件
2.2 方案实现
2.2.1 定义服务bean模型&接口
我们先看下plantUML的e-r图大概是长啥样的,同时可以基于此做数据模型的适配和集成。如下图是一个简单的数据库表描述:
左边是文件内容,右边则是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);
}
运行上述代码可看到下面的效果图:
2.2.4 api形式提供服务
目前这个功能已经集成到codeMaker-core中了,可以启动服务访问接口/getproject/erpicture则可以看到效果。
2.2.5 spring 集成提供服务
由于是基于接口实现,本工具类并没有集成spring框架,但是可以将接口ErPictureService注册为spring bean.
我最近整了一个公众号,持续输出原创内容,敬请关注: