项目源码地址: github.com/wangyuheng/…背景利用plantuml绘制架构评审图时,发现数据库ER图手写字段信息成本太大,需要一个把DB表结构转换为plantuml格式的工具。搜索
项目源码地址: github.com/wangyuheng/…
背景
利用plantuml
绘制架构评审图时,发现数据库ER图手写字段信息成本太大,需要一个把DB表结构转换为plantuml
格式的工具。搜索了一番,没有发现支持工具,所以准备手撸一个,并记录下设计及编码实现的过程。
需求
一句话需求: 读取数据库表结构,转换为plantuml格式的ER图。
设计
根据需求抽象出下列概念
基础元素(一个输入一个输出)
ER
->plantuml
格式er
图db_schema
-> 数据库结构,ddl语句是其中一种实现形式
扩展概念
table
-> 表结构信息template
-> 模板,定义er
图输出格式
操作行为
reader
-> 读取数据库结构,识别table
parser
-> 根据table
和template
转换为ER
图writer
-> 输出ER
图文件
整体交互
storage "Context" { node ddl node db_schema node ER node template node table usecase parser usecase reader usecase writer}ddl -down-|> db_schemadb_schema -down-> readerreader -> tabletable -> parserparser <-up- templateparser -down-> writerwriter -> ER
选型
都是基本的文件及String操作,通过 druid 进行sql解析
编码实现
Reader
读取ddl.sql文件,解析成 table
interface Reader { fun read(dbType: String? = DEFAULT_DB_TYPE): Iterable<Table> fun extract(dbType: String, sql: String): Table { ... }}class FileReader(private val path: String) : Reader { override fun read(dbType: String?): Iterable<Table> { return Files.readAllLines(Paths.get(path)) .filter { !it.startsWith("#") } .joinToString("") .split(";") .filter { it.isNotBlank() } .map { extract(dbType?: DEFAULT_DB_TYPE, it) } .toList() }}
Writer
template
通过resource文件管理,接收table
输出plantuml格式ER
图
interface Writer { fun write(tables: Iterable<Table>) fun parse(tables: Iterable<Table>): String { val template = Thread.currentThread().contextClassLoader.getResource("dot.template")!!.readText() val content = tables.joinToString("") { table -> val columns = table.columnList.joinToString("/n") { "${it.notNullNameWrapper()} ${it.type} ${it.defaultValue} ${it.comment}" } "Table(${table.name}, /"${table.name}//n(${table.comment})/"){ /n $columns + /n } /n" } return template.replace("__content__", content) } private fun Column.notNullNameWrapper(): String { return if (this.notNull) { "not_null(${this.name})" } else { this.name } }}class FileWriter(private val path: String) : Writer { override fun write(tables: Iterable<Table>) { Files.write(Paths.get(path), parse(tables).toByteArray()) }}
Main
fun main(args: Array<String>) { val inPath = args[0] val outPath = args[1] val dbType = args.getOrNull(2) FileReader(inPath).read(dbType) .apply { FileWriter(outPath).write(this) }}
效果
java -jar ddl2plantuml.jar ddl.sql er.puml
程序会读取当前目录下的ddl.sql文件,并转换生成er.puml文件。
@startuml!define Table(name,desc) class name as "desc" << (T,#FFAAAA) >>!define primary_key(x) <color:red><b>x</b></color>!define unique(x) <color:green>x</color>!define not_null(x) <u>x</u>hide methodshide stereotypesTable(table_1, "table_1/n(This is table 1)"){ not_null(id) bigint column_1 not_null(prod_name) varchar column_2 not_null(prod_type) tinyint '0' column_3 0:活期 1:定期 not_null(start_time) time 停止交易开始时间 not_null(end_time) time 停止交易结束时间 not_null(online_type) tinyint '0' 0:上线 1:未上线 not_null(prod_info) varchar '' 产品介绍 not_null(over_limit) tinyint '0' 超额限制 0:限制 1:不限制 not_null(created_time) datetime CURRENT_TIMESTAMP not_null(updated_time) datetime CURRENT_TIMESTAMP } Table(table_2, "table_2/n(This is table 2)"){ not_null(id) bigint not_null(user_id) bigint 用户id not_null(user_name) varchar 用户名称 not_null(prod_id) bigint 产品id interest_date dateNULL计息日期 not_null(created_time) datetime CURRENT_TIMESTAMP 创建时间 not_null(updated_time) datetime CURRENT_TIMESTAMP 更新时间 } Table(table_3, "table_3/n(This is table 3)"){ not_null(id) bigint not_null(user_id) bigint 用户id not_null(user_name) varchar 用户名称 not_null(prod_id) bigint 产品id interest_date dateNULL计息日期 not_null(created_time) datetime CURRENT_TIMESTAMP 创建时间 not_null(updated_time) datetime CURRENT_TIMESTAMP 更新时间 } @enduml
.
.

- 0