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生成、非破坏性变更检测、版本快照回滚等设计,实现了生产环境可靠的零代码表结构管理。开发者可直接复用此机制,或二次开发自定义字段类型。

相关推荐
van久1 小时前
Day30:Redis 缓存策略 + 菜单实战缓存 + 三大缓存问题(穿透 / 击穿 / 雪崩)
数据库·redis·缓存
2501_901200531 小时前
Laravel 大批量数据填充时的内存泄漏与性能优化指南
jvm·数据库·python
ID_180079054731 小时前
除了JSON,淘宝店铺商品API接口还支持哪些数据格式?
android·数据库
newnazi1 小时前
RedHart安装Oracle 12C
数据库·oracle
霸道流氓气质2 小时前
Spring AI ChatMemory 对话记忆配置JDBC方式到Mysql数据库实战示例与原理讲解
数据库·人工智能·spring
与数据交流的路上2 小时前
Redis-jedis连接池配置错误导致Redis CPU飙高
数据库·redis·缓存
杂家2 小时前
Windows部署Redis
数据库·windows·redis
IpdataCloud2 小时前
电商防刷单:如何用IP风险识别工具拦截虚假交易?实操指南
数据库
m0_740796362 小时前
golang如何实现工作流引擎_golang工作流引擎实现要点
jvm·数据库·python