Skip to content

数据建模 · 总览

数据建模是 forge 框架的地基:你用一个个普通的 Java 类描述业务实体,框架在启动期解析注解、推导字段、生成数据库表,并把模型元数据落库;运行期再通过统一的 CRUD 门面与关系字段读写数据。一句话概括——以 Java 模型为单一事实来源,schema 与元数据自动派生。

本章面向业务模块开发者,所有教学篇都复用同一套「宠物商店」实体,字段名保持一致,便于你边读边照抄。

宠物商店领域

我们用一个宠物商店贯穿全章,实体与关系如下。

实体角色关键字段
Category宠物分类id, name, code
Owner主人id, name, phone(O2M 持有 pets)
PetProfile健康档案id, vaccine, weight(与 Pet O2O,外键 profileId 落在 Pet 侧)
Tag标签id, name(与 Pet M2M)
PetTag中间表petId, tagId(extends RelationModel
Pet主角name、status、price、categoryId、ownerId、profileId,以及关系字段 category/owner/profile/tags

其中 Pet.status 是一个落库枚举 PetStatus,取值 ON_SALE(1,"在售")、SOLD(2,"已售")、OFF_SHELF(3,"下架"),implements ValueEnum<Integer>

双向关系字段的防坑约定

凡是双向引用的关系字段(如 Pet.ownerOwner.pets),关系字段上务必加 @EqualsAndHashCode.Exclude@ToString.Exclude,否则 Lombok 生成的 equals/hashCode/toString 会沿引用环互相调用,导致 StackOverflow。详见 关系字段

能力地图

从「定义一个模型」到「读写带关系的数据」,整条链路分六层能力,建议按此顺序阅读。

能力做什么对应篇
模型定义@Model 把类声明为模型,继承 IdModel<T> 拿主键与审计字段快速开始模型注解
字段 / 枚举@Field 系列声明列类型,落库枚举须实现 ValueEnum<T>枚举与字段
CRUD 门面通过 Models 静态门面做查询、单实体写、批量写CRUD 操作
关系字段O2O / M2O / O2M / M2M 的读填充与写维护关系字段
逻辑删除deleted 时间戳软删、删列删表归档语义逻辑删除
元数据落库启动期把模块/模型/字段写入系统表模型注解逻辑删除

章节导航

文档你能学到
快速开始从零定义第一个 Pet 模型并启动,看到预期日志
模型注解@Model / @Model.Advanced 全参数、索引与唯一键、模型类型
枚举与字段@Field 各类型子注解,ValueEnum<T> 契约与 EnumStoreType 推断
CRUD 操作Models.origin / of / ofBatch 真实签名与用法
关系字段四类关系的声明、读填充、写维护与保存形态
逻辑删除forge.model.logic-deleteforge.ddl 配置、删列删表语义
API 速查门面方法、注解参数、定义类字段一页速查

一个最小闭环

下面这段代码展示从写实体到读写数据的最小闭环,细节在各篇展开。

java
// 1. 定义模型(节选)
@Data
@EqualsAndHashCode(callSuper = true)
@Model(name = "pet.Pet", displayName = "宠物")
public class Pet extends IdModel<Pet> {

    @Field(displayName = "名称")
    @Field.String(length = 64)
    private String name;

    @Field(displayName = "状态")
    @Field.Enum
    private PetStatus status;

    @Field(displayName = "分类ID")
    @Field.Long
    private Long categoryId;

    @Field.M2O
    @Field.Relation(relationFields = "categoryId")
    @EqualsAndHashCode.Exclude @ToString.Exclude
    private Category category;
}

// 2. 写一只宠物
Pet pet = new Pet();
pet.setName("旺财");
pet.setStatus(PetStatus.ON_SALE);
pet.setCategoryId(1L);
Models.of(pet).save();

// 3. 查询并填充关系字段
Pet got = Models.origin(Pet.class).queryById(pet.getId());
Models.fieldQuery(got, "category");

看完本章后下一步

  • 想理解「为什么这么设计」 → 设计文档
  • 看到不认识的反射 / 包扫描 / 启动钩子机制 → 前置知识