摘要:本文以「预算项目 → 类别清单 → 费用明细项」三级业务为例,讲解 VeapAI 在线开发中多表关联的建模方法。先梳理关联关系再选择形态,重点介绍主子表与树表两种页面形态,说明级联删除 SQL 在生成期如何由模板按外键标记生成,并对比「建模事实、渲染配置、渲染实现」分层设计与「全配置塞一张 JSON」的差异,最后给出判断何时不该用主子表的标准与复现步骤。
> 关键词:主子表建模、树表、级联删除 | 首发:CSDN
上一篇文章讲了生成器的三种形态怎么渲染,这篇换个视角:拿到一个多表关联业务,怎么在在线开发里把它建出来。用一个真实场景贯穿——"预算项目 → 类别清单 → 费用明细项"。
## 先想清楚关联,再选形态
这个业务三级:一个预算项目挂多个类别,一个类别挂多个明细。建表三张,外键两个:`类别表.project_id -> 项目表.id`、`明细表.category_id -> 类别表.id`。
在线开发里怎么建?两个方案:
- 方案 A:三层各建一个单表页面,用户在三个页面之间跳转;
- 方案 B:项目作为主表,类别挂成子表(主子表),明细再作为类别的子表,一个页面里三层联动。
VeapAI 支持 B。`veap_generate` 表的建模字段:
| 字段 | 说明 |
| --- | --- |
| `sub_table_name` | 子表名称 |
| `sub_table_fk_name` | 子表关联的外键名 |
| `master_sub_schema` | 主子配置 |
| `tree_schema` | 树配置(树表形态用) |
## 主子表页面长什么样
![主子表编辑页]
主表编辑页内嵌子表表格,子表行内编辑。保存时主表一行、子表多行,一个事务提交。这是 `SubTableRenderSpec` 渲染出来的标准形态,不需要手写嵌套表单代码。
树表适用于"部门树、分类树"这类自带层级的数据,`tree_schema` 描述父字段、排序字段。树初始化逻辑在 `TreeInitializer`。
![树表页面]
## 级联删除:生成期就写好的 SQL
主子表的删除语义在 `mapper.xml.vm` 里处理。模板里有一段判断(L374 附近):
```velocity
## 仅对子表外键(SUB)生成按外键删除 SQL
#if("$fk.type" == "SUB")
```
主表删除时,按外键先删子表数据。这是生成期就决定的 SQL,不是运行期拼的。外键关系在建模时被显式标记为 `SUB`,模板据此生成联动删除。
## 一处较真的设计
建模和渲染分两层:`veap_generate.sub_table_name / sub_table_fk_name` 是建模事实,`master_sub_schema` 是渲染配置,`SubTableRenderSpec` 才是渲染实现。三层各管各的,改渲染不改建模,改建模不动渲染。
反面的例子是"全配置塞一张 JSON":主子结构、字段顺序、页面布局混在一个大 JSON 里,改个列宽要全文 diff。VeapAI 把建模字段做成独立的列,JSON 只留纯渲染配置。
![多表关联建模决策]
## 什么时候不用主子表
不是所有多表关联都该主子表。判断标准:子表数据生命周期跟随主表(主表删、子表删)、页面同屏编辑、子表量大但单主表行数可控。反过来,跨业务引用的关联(如项目引用供应商)应该用字段级配置(`dict_type` 字典、`component_props_json` 远端加载)解决,而不是挂成子表。建模错了,页面和 SQL 都跟着难受。
## 复现
```bash
cd veap-cloud
mvn -B -pl veap-package/veap-monolith -am -DskipTests package
cd veap-ui && npm install && npm run dev
```
按上面的三级业务建三张表,导入在线开发,把类别挂到项目下、明细挂到类别下,生成后删除一条项目数据,观察 `mapper.xml` 里的级联删除 SQL 执行顺序。
下一篇进入流程部分:Flowable-first 扩展配置架构的三层模型。
建模决策的依据在 `veap_generate` 表注释与 `mapper.xml.vm`,对照三级业务跑一遍就有体感:https://gitee.com/mindock/veap