JVS低代码动态建表原理:从模型配置到数据库DDL的生成逻辑

摘要区:

JVS低代码的数据模型引擎无需手动建表,通过可视化配置自动生成数据库表结构。本文从源码层面解析其实现:模型配置JSON如何转换为DDL语句,如何处理字段类型映射、索引、外键,以及版本升级时的增量变更策略。


一、问题的提出

传统开发中,每新增一个业务模块,都需要手动编写CREATE TABLE、ALTER TABLE等DDL语句,并与实体类保持同步。JVS低代码的数据模型引擎实现了"配置即表结构"的能力,其核心挑战在于:

  • 如何将用户拖拽配置的字段(文本、数字、日期、选项等)映射为MySQL/PostgreSQL等数据库的数据类型?

  • 如何保证模型修改后,存量数据不丢失?

  • 如何处理字段重命名、删除等破坏性操作?

本文将以JVS低代码v2.5源码为基础,分步骤解析动态建表的完整逻辑。

二、整体流程概览

用户在前端配置数据模型 → 保存模型JSON → 后端解析 → 生成Diff变更计划 → 执行DDL → 更新模型版本号。

核心类位于 jvs-modules/data-model 模块:

text

复制代码
com.jvs.dm.service.ModelDDLService
├── generateCreateDDL()      // 新建模型时生成CREATE TABLE
├── generateAlterDDL()       // 修改模型时生成ALTER TABLE
├── columnTypeMapping()      // 类型映射
└── executeWithVersion()     // 执行DDL并记录版本
三、关键源码解析

3.1 字段类型映射规则

JVS定义了一套抽象数据类型(JvsFieldType),然后根据数据库方言转换:

java

复制代码
public enum JvsFieldType {
    TEXT,      // 短文本 → VARCHAR(255)
    LONG_TEXT, // 长文本 → TEXT
    NUMBER,    // 数字 → DECIMAL(20,4)
    INTEGER,   // 整数 → BIGINT
    DATE,      // 日期 → DATE
    DATETIME,  // 日期时间 → DATETIME(3)
    BOOLEAN,   // 布尔 → TINYINT(1)
    OPTION,    // 选项 → VARCHAR(100)
    FILE,      // 附件 → VARCHAR(500) 存储路径
    REFERENCE; // 关联 → BIGINT 存储关联ID
}

转换核心代码(简化版):

java

复制代码
public String toColumnDefinition(JvsField field, Dialect dialect) {
    String type = field.getType().getSqlType(dialect);
    StringBuilder sb = new StringBuilder();
    sb.append(field.getName()).append(" ").append(type);
    if (field.isRequired()) sb.append(" NOT NULL");
    if (field.getDefaultValue() != null) sb.append(" DEFAULT '").append(field.getDefaultValue()).append("'");
    return sb.toString();
}

3.2 Diff算法:模型变更检测

当用户修改模型(新增/修改/删除字段)时,引擎会比较旧模型JSON与新模型JSON,生成变更列表:

java

复制代码
public List<SchemaChange> diff(Model oldModel, Model newModel) {
    List<SchemaChange> changes = new ArrayList<>();
    // 新增字段
    for (Field newField : newModel.getFields()) {
        if (oldModel.getField(newField.getName()) == null) {
            changes.add(new AddColumnChange(newField));
        }
    }
    // 删除字段:将字段标记为deleted(不实际删除列,避免数据丢失)
    for (Field oldField : oldModel.getFields()) {
        if (newModel.getField(oldField.getName()) == null) {
            changes.add(new SoftDeleteColumnChange(oldField));
        }
    }
    // 修改字段(类型、长度、是否必填)
    // ... 使用比较器
    return changes;
}

实际执行DDL时,对删除字段的处理:

  • 不执行 DROP COLUMN,而是重命名为 {field_name}_deleted_{timestamp},并记录到 deleted_columns 元数据表中。

  • 这样既避免了数据丢失,又允许用户随时恢复。

3.3 版本控制与回滚

每次执行DDL后,系统会在 jvs_model_version 表中记录:

字段 说明
model_id 模型ID
version 版本号(自增)
ddl_sql 执行的DDL语句
schema_snapshot 完整表结构的JSON快照
created_at 执行时间

用户可通过管理后台回滚到任意历史版本,系统会根据 schema_snapshot 反向生成回滚DDL。

四、扩展实践:自定义字段类型

如果默认类型不满足需求(如需要GIS地理类型),可通过插件机制注册:

  1. 实现 CustomTypeHandler 接口。

  2. 在 resources/META-INF/services/com.jvs.dm.CustomTypeHandler 中注册。

  3. 前端组件库中增加对应的编辑器。

示例:注册PostgreSQL的 GEOMETRY 类型:

java

复制代码
public class GeometryTypeHandler implements CustomTypeHandler {
    @Override
    public String getTypeName() { return "GEOMETRY"; }
    @Override
    public String toSqlType(Dialect dialect) {
        return "GEOMETRY";
    }
}
五、总结

JVS低代码的动态建表引擎通过模型驱动DDL生成、非破坏性变更检测、版本快照回滚等设计,实现了生产环境可靠的零代码表结构管理。开发者可直接复用此机制,或二次开发自定义字段类型。

相关推荐
TDengine (老段)3 小时前
TDengine IDMP 十大亮点功能(一)
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
为思念酝酿的痛3 小时前
MySQL的基本查询
数据库·mysql
花间相见3 小时前
【计算基础|Redis01】【上手】安装连接、配置与通用命令
数据库·后端·面试
geovindu3 小时前
Pivoting and Unpivoting Data in SQL using postgreSQL 18
大数据·数据库·数据仓库·sql·postgresql·数据库开发·数据库架构
我命由我123453 小时前
6-3.Python 异常处理 - 异常处理进阶(else 块与 finally 块、异常传递、自定义异常)
开发语言·前端·数据库·后端·python·学习·java-ee
Elastic 中国社区官方博客3 小时前
Elastic 重大事件功能如何将 100 条日志样本转化为检测规则
大数据·运维·数据库·人工智能·elasticsearch·搜索引擎
adinnet20263 小时前
持续智能问数:多轮对话状态管理、下钻钻取与经营驾驶舱的演进
javascript·数据库·人工智能
系统集成-鹰翔4 小时前
ERP接口月度统计少了零单月份:SQLite分组与最近三个月均值排查
javascript·数据库·经验分享·后端
ClouGence4 小时前
支付机构为什么需要数据同步:从监管报送到跨云业务协作
数据库·数据分析