低代码设计器里加了一个字段、把 varchar(32) 拉到 varchar(128),点了保存------业务页面好像有了。 打开库一看:列还是旧的。再点「同步数据库」,弹窗要么灰掉,要么提示「高风险,请导出脚本」。 很多人卡在这里:到底哪些变更能在线跑,哪些必须二次确认?
这篇只跟做一件事:预览差异 → 安全 DDL 直接同步 → 高风险强制执行(仍禁止删表、清空、删列)。 方案载体是 Forge Admin 业务对象设计器;标题用通用词方便检索。文中路径相对仓库 forge-project,推断处标「按源码推演」,发前建议在自己环境用一张非生产表复验。
查重:与已发的 FlowStart 表单挂审批、流程超时终态、门户入口权限都不是同一角度;作废不发的 designPreview 卡死稿也不复用。
TL;DR
- 保存设计草稿不等于改库:预览接口只算差异,不执行 DDL(单测 previewNeverExecutesDdl)。
- 预览会拆两计数:onlineSafeDdlCount(可在线)与 manualReviewDdlCount(高风险)。
- 普通同步:二次确认 confirmOnlineDdl=true 后只跑安全语句(加表、加列、索引、兼容扩容)。
- 强制执行:forceExecute=true 可跑预览里的高风险 MODIFY;仍拒绝删表、清空、删列。
- 门禁:数据源 allowDdl、权限 ai:lowcode:deploy-ddl、设计版本一致,缺一不可。
一、现象:设计器有字段,库里没有
常见三条「半成品」:
- 设计器字段面板能看见新列,列表与表单预览也有,但查看建表语句时没有新列。
- 点同步提示「仅预览 / 人工审核」,或「当前差异均为高风险变更」。
- 混着加列和改类型:想一次点完,结果安全变更也没落库。
根因通常不是「低代码不能改表」,而是:变更被分成可在线与需审核两类,默认只放行追加式与兼容扩容。 Forge Admin 在提交 da2bf3595 把这件事做透了------先预览计数,再决定「确认同步」还是「强制执行全部」。
二、全景:四步落地

text
设计器改字段 / 改长度
| 保存草稿(不执行 DDL)
v
POST .../database-diff -> LowcodeDdlPreviewVO
| onlineSafeDdlCount / manualReviewDdlCount / warnings
v
+-------------------+ +----------------------------------+
| 确认同步(安全) | | 强制执行全部(含高风险) |
| confirmOnlineDdl | | confirmOnlineDdl + forceExecute |
| executeSafeDdlOnly| | executePreviewDdl(..., true) |
+-------------------+ +----------------------------------+
| |
+---- 仍禁止破坏性语句 -----+
| 层级 | 真实路径 |
|---|---|
| 预览 VO | .../vo/lowcode/LowcodeDdlPreviewVO.java(onlineSafeDdlCount / manualReviewDdlCount) |
| 白名单 | .../lowcode/LowcodeOnlineDdlPolicy.java(isSafeOnlineDdl / assertSafeDdl) |
| 执行 | .../lowcode/LowcodeDdlService.java(enrichOnlineSafetyCounts / executeSafeDdlOnly / executePreviewDdl / assertNotDestructiveDdl) |
| 同步门禁 | .../businessapp/BusinessObjectTableMappingService.java(syncDatabase confirm/force) |
| API | BusinessObjectDesignerController:POST /{objectId}/database-diff、/database-sync |
| UI | BusinessTableMappingSummary.vue(预览弹窗、确认同步、强制执行全部、导出脚本) |
| 前端 API | forge-admin-ui/src/api/business-application.js → syncBusinessObjectDatabase |
| 单测 | BusinessObjectDatabaseSyncServiceTest.java(预览不执行、需确认、强制跑高风险等) |
权限码:ai:lowcode:deploy-ddl(Controller 上 @SaCheckPermission,Service 内 DDL_PERMISSION 再校验一次)。
三、跟做:从预览到同步
3.1 打开「数据结构」,先预览
在应用中心业务对象设计器展开数据结构面板,点预览差异。前端会先刷新 mapping,再带上当前 designVersion 调差异接口。
同步接口(真正改库时)大致是:
javascript
// business-application.js
export function syncBusinessObjectDatabase(objectId, designVersion, options = {}) {
return request.post("/ai/business/object/" + objectId + "/database-sync", {
designVersion,
confirmOnlineDdl: true,
forceExecute: options.forceExecute === true,
}, ENCRYPTED_REQUEST)
}
预览走 database-diff,不带执行。后端 previewDatabaseDiff 到 LowcodeDdlService.previewCreateTable,最后 enrichOnlineSafetyCounts:
- 命中 isSafeOnlineDdl(建表 / 加列 / 加索引 / 注释)→ 计入 safe;
- 或属于「兼容扩容」集合(同类型加长、放宽或收紧可空等,见 buildCompatibleModifyStatements)→ 也算 safe;
- 其余(改类型族、缩短长度等)→ manual。
UI 文案会变成类似:「可在线同步 N 条,另有 M 条可强制执行」。
按源码推演:保存设计草稿本身不会执行这些变更------确认弹窗文案写得很直白。漏点同步,页面协议有字段、物理表没有,是预期行为不是玄学。
3.2 安全变更:点「确认同步」
当 onlineSafeDdlCount > 0 且数据源允许在线 DDL 时,主按钮可点。BusinessObjectTableMappingService.syncDatabase 要求:
- confirmOnlineDdl == true,否则直接「需要显式二次确认」;
- 有 ai:lowcode:deploy-ddl;
- designVersion 与当前草稿一致;
- 数据源 allowDdl;
- 预览 executable == true。
普通同步调用 executeSafeDdlOnly:只执行安全与兼容扩容语句。若同时存在高风险项,状态记 PARTIAL,提示「已在线同步 X 条;另有 Y 条可导出或强制执行」------不会因为一条 unsafe 把整单安全变更挡死。
白名单核心(LowcodeOnlineDdlPolicy.isSafeOnlineDdl)可概括为:
| 语句形态 | 是否默认在线安全 |
|---|---|
| 建表(含 IF NOT EXISTS) | 是 |
| 改表加列 | 是 |
| 加普通/唯一索引 | 是 |
| 表/列注释 | 是 |
| 同类型加长、兼容可空变更(兼容集合) | 是(按源码:走 compatibleModifies) |
| 改类型族、缩短长度等 | 否 → manual |
| 删表 / 清空表 / 删列 | 执行层直接拒绝 |
3.3 高风险:用「强制执行全部」,或导出给 DBA
预览里 manualReviewDdlCount > 0 时,UI 出现警告色按钮「强制执行全部」。弹窗是 dialog.error,文案明确:可能导致截断或失败,请备份;仍禁止破坏性语句。
后端 forceExecute=true 时走 executePreviewDdl(modelSchema, true):跳过追加式白名单,但仍先 assertNotDestructiveDdl------删表、删列、清空一律抛业务异常。
DTO 注释写清契约:forceExecute 必须与 confirmOnlineDdl=true 同时传入;仍禁止破坏性语句。源码在 assertNotDestructiveDdl 里按语句前缀硬拒绝。
也可以点导出脚本:前端把预览语句拼成 .sql 下载(带表名与 design version 注释),交给 DBA 审核------和生产变更流程兼容。
3.4 和「托管表单自动同步」的差别
页面表单托管存储还会走 syncManagedDatabase:元数据事务提交后自动跑安全 DDL,高风险跳过并记 PARTIAL,不阻断保存。普通业务对象必须走带确认的 syncDatabase------别把「表单自动补列」和「高级数据设置里手动同步」混成一个按钮。
四、对照表:该点哪个按钮
| 场景 | 建议动作 | 结果口径 |
|---|---|---|
| 只加字段 / 新建表 / 加索引 | 确认同步 | 期望 IN_SYNC |
| 同类型加长、调整可空 | 确认同步 | 走兼容扩容,算安全 |
| 加列 + 改类型族混在一起 | 先确认同步,再强制或导出 | 先 PARTIAL,再处理 manual |
| 只有缩长度 / 改类型族 | 强制执行或导出 | 普通同步会整单拒绝 |
| 想删列 / 删表 | 不要指望在线按钮 | 执行层拒绝;导出后由 DBA 审 |
五、自查清单(建议收藏)

- 运行数据源勾了允许在线 DDL(allowDdl);只读库会预览但不可执行。
- 账号有 ai:lowcode:deploy-ddl,不只是设计权限。
- 预览前 designVersion 与草稿一致;过期版本同步会被拒。
- 先看 onlineSafeDdlCount / manualReviewDdlCount,再决定按钮。
- 普通同步成功但状态 PARTIAL:去处理剩余高风险,或导出脚本。
- 强制执行前已备份;接受截断/失败风险。
- 任何入口都执行不了删表/清空------这是特性不是 bug。
- 非生产表先复验一遍;生产大表评估锁表窗口(平台不替代 gh-ost)。
五(续)、跟做小场景:加列 + 改长度混单
假设业务对象已有物理表,你在设计器做了两件事:新增字段 remark,并把 name 从 varchar(32) 调到 varchar(128)。点预览后常见结果是:
- onlineSafeDdlCount 至少包含 ADD COLUMN remark;若 name 加长被判定为兼容扩容,也会进 safe。
- 若你把 name 改成了另一类型族(例如改成 text 或整型),则这条进 manualReviewDdlCount。
推荐操作顺序:先点「确认同步」把 safe 落库,状态若为 PARTIAL,再决定「强制执行全部」还是导出脚本给 DBA。不要一上来就点强制------把可在线的追加变更也绑进高风险确认,只会增加误操作面。
常见坑(按源码行为)
- 只保存设计、不同步:列表协议有字段,查询报「未知列」。先看预览计数,再确认同步。
- 有设计权、无 deploy-ddl:按钮灰或接口直接拒;找管理员加 ai:lowcode:deploy-ddl。
- 数据源未开 allowDdl:预览能出语句,执行口直接关;只读备库常见。
- designVersion 过期:别人刚发过版或你本地草稿已变;刷新映射后再预览。
- 把托管表单的自动补列,当成高级数据设置的手动同步:两条链路门禁不同,混用会误判「怎么有时自动有时要确认」。
若你维护的是生产大表,即使语句落在 safe 白名单,也建议先在预发用同等数据量估锁表窗口;Forge Admin 提供的是设计器侧的门禁与分账,不是在线改表引擎本身(不替代第三方在线变更工具)。把「能不能点」和「该不该在高峰点」分开看,运维会轻松很多。
六、小结 & CTA
低代码「加字段」真正落地,靠的不是一个万能改表语句,而是三道闸:
- 预览分账:安全条数 vs 高风险条数摊在桌面上;
- 默认同步只追加/兼容扩容,避免一条 unsafe 拖死整单;
- 强制执行有二次确认,破坏性语句永拒。
如果你也在做「设计器改模型 → 物理表跟着变」,欢迎对照这套门禁看自己的平台缺了哪一闸。觉得有用请点赞收藏,少在测试库误跑一条缩长度;评论区可以丢你卡过的 DDL 场景,我按票数排下一篇跟做。
- Gitee:gitee.com/ForgeLab/fo...
- GitHub:github.com/yaomindong1...
- 文档:www.dlforgelab.com:8084/forge-docs/
- 演示:www.dlforgelab.com:8084/forge/login (admin / 123456)