搜索 K
Appearance
Appearance
数据建模是 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.owner 与 Owner.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-delete 与 forge.ddl 配置、删列删表语义 |
| API 速查 | 门面方法、注解参数、定义类字段一页速查 |
下面这段代码展示从写实体到读写数据的最小闭环,细节在各篇展开。
// 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");