项目源码地址: github
背景
利用 plantuml
绘制架构评审图时,发现数据库 ER 图手写字段信息成本太大,想要一个把表结构转换为 plantuml
格式的工具。
搜索了一番,没有发现支持工具,所以准备手撸一个,并记录下设计及编码实现的过程。
需求
一句话需求: 读取数据库结构,转换为 plantuml 格式的 ER 图。
设计
根据需求抽象出下列概念
基础元素(一个输入一个输出)
ER
->plantuml
格式er
图db_schema
-> 数据库结构,ddl 语句是其中一种实现形式
扩展概念
table
-> 表结构信息template
-> 模板,定义er
图输出格式
操作行为
reader
-> 读取数据库结构,识别table
parser
-> 根据table
和template
转换为ER
图writer
-> 输出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 methods
hide stereotypes
Table(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
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于