ER 图 / 数据模型图通常是数据库设计的第一步。PlantUML 在这块语法比 Mermaid 略弱,但能用,且能跟其他 UML 图保持一致风格。
一句话定义 ER 图描绘数据库表(实体)之间的关联关系:1:1 / 1:N / N:M、弱实体、派生属性。它是从领域模型 到物理模型 过渡的标准图。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 @startuml hide circle entity User { *id : BIGINT <<PK>> -- *email : VARCHAR(255) <<UK>> name : VARCHAR(100) status : ENUM('active', 'banned', 'pending') created_at : TIMESTAMP } entity Order { *id : BIGINT <<PK>> -- *user_id : BIGINT <<FK>> *status : ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') total : DECIMAL(10,2) created_at : TIMESTAMP } entity OrderItem { *id : BIGINT <<PK>> -- *order_id : BIGINT <<FK>> *product_id : BIGINT <<FK>> quantity : INT unit_price : DECIMAL(10,2) } entity Product { *id : BIGINT <<PK>> -- *sku : VARCHAR(50) <<UK>> name : VARCHAR(255) price : DECIMAL(10,2) stock : INT } User ||--o{ Order : 创建 Order ||--|{ OrderItem : 包含 Product ||--o{ OrderItem : 出现在 note top of User ER 图符号: || —— 恰好一个 |o —— 0 或 1 }o —— 多 { —— 必填 end note @enduml
三种核心关系符号 1 2 3 4 5 A ||--|| B : A 对 B 一对一(exactly) A ||--o{ B : A 对 B 一对多(A 端多实例,B 端可有 0 个) A }o--o{ B : A 对 B 多对多 A ||--|{ B : A 对 B 一对多(B 端必须至少一个) A }|--|{ B : A 对 B 多对多(A、B 至少一个)
记忆口诀:
左侧
右侧
表示
`
`
`
`
`
`
}o
}o
多对多
衍生关系 自关联 1 2 3 4 5 6 7 8 9 10 11 12 13 @startuml hide circle entity Employee { *id : BIGINT <<PK>> -- name : VARCHAR *manager_id : BIGINT <<FK>> } Employee ||--o{ Employee : 管理 @enduml
员工表「manager_id」指向自己的「id」,表达上下级关系。
派生属性(虚线边框或省略号) PlantUML 不直接支持「派生」标记。但可以用 <<computed>> 或者用注释说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @startuml entity Order { *id : BIGINT <<PK>> -- *user_id : BIGINT <<FK>> subtotal : DECIMAL(10,2) tax : DECIMAL(10,2) -- total : DECIMAL(10,2) <<computed>> } note right of Order total = subtotal + tax 触发器或者应用层维护 end note @enduml
多对多的连接表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @startuml hide circle entity Student { *id : BIGINT <<PK>> name : VARCHAR } entity Course { *id : BIGINT <<PK>> title : VARCHAR } entity Enrollment { *id : BIGINT <<PK>> -- *student_id : BIGINT <<FK>> *course_id : BIGINT <<FK>> grade : DECIMAL(5,2) enrolled_at : TIMESTAMP } Student ||--o{ Enrollment : "报名" Course ||--o{ Enrollment : "被报名" @enduml
连接表(也叫「关联表」「联结表」)描述多对多关系。
实战:博客系统数据模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 @startuml hide circle entity User { *id : BIGINT <<PK>> -- *email : VARCHAR(255) <<UK>> name : VARCHAR(100) bio : TEXT role : ENUM('admin','editor','member') created_at : TIMESTAMP updated_at : TIMESTAMP } entity Post { *id : BIGINT <<PK>> -- *author_id : BIGINT <<FK>> *slug : VARCHAR(255) <<UK>> *title : VARCHAR(500) *body : TEXT *status : ENUM('draft','published','archived') published_at : TIMESTAMP created_at : TIMESTAMP updated_at : TIMESTAMP } entity Tag { *id : BIGINT <<PK>> -- *name : VARCHAR(50) <<UK>> description : VARCHAR(255) } entity PostTag { *post_id : BIGINT <<FK>> *tag_id : BIGINT <<FK>> -- } entity Comment { *id : BIGINT <<PK>> -- *post_id : BIGINT <<FK>> *author_id : BIGINT <<FK>> *body : TEXT created_at : TIMESTAMP } User ||--o{ Post : "撰写" User ||--o{ Comment : "留下" Post ||--o{ Comment : "拥有" Post ||--o{ PostTag : "被分入" Tag ||--o{ PostTag : "标记" @enduml
评审 checklist
工具对比
工具
优势
劣势
PlantUML ER
文本化,diff 友好,统一 PlantUML 风格
复杂布局一般
Mermaid erDiagram
GitHub 原生渲染
语法有限
draw.io
WYSIWYG,自由布局
文件不是文本
dbdiagram.io
专门的 ER DSL
输出 PNG/SVG 不能 merge
MySQL Workbench / DataGrip
完整 DDL 双向
重,需要 Java / 桌面安装
实战:从 ER 图生成 SQL DDL PlantUML 不直接生成 DDL,但你可以把 ER 图作为「设计文件」,CI 用 script 生成 DDL(之后用 Flyway / Liquibase 跑迁移):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 name: Generate DDL from ER on: paths: ['docs/erd/**' ] jobs: ddl: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Render ER diagrams run: | # 自动将 .puml ER 图转 .svg 供 review ... - name: Hand-write / review migrations if: github.event_name == 'pull_request' run: | # CI 检查 docs/erd/*.puml 与 migrations/*.sql 一致 ...
注意:ER 图 → DDL 不是无脑的。索引、约束、默认值、字符集都要追加,由 DBA 或者迁移工具决定。