关联
关联把表与表连起来。四种方向,一句话记住谁拥有外键:
| 关联 | 外键位置 | 本模型视角 |
|---|---|---|
m2o(多对一) | 本表(拥有方) | 带一个关联对象(store) |
o2m(一对多) | 对方表 | 带一个集合(books) |
o2o(一对一) | 其中一侧(拥有方) | 带一个关联对象 |
m2m(多对多) | 中间表 | 带一个集合(authors) |
多对一与一对多:同一张外键
// ① 书与书店:多对一在 Book(外键 STORE_ID),一对多在 BookStore(纯反向)
const BookStore = model("BookStore", "id", class {
id = prop.i64()
name = prop.str(100)
books = prop.o2m(Book).mappedBy("store")
.orderBy("name", "edition") // 集合默认排序(取 books 时的默认顺序)
})
const Book = model("Book", "id", class {
id = prop.i64()
name = prop.str(50)
edition = prop.i32()
price = prop.num(10, 2)
// 拥有方:生成外键列 STORE_ID;.nullable() = 外键可空
store = prop.m2o(BookStore)
.joinColumns({ cascade: "DELETE" }) // 删书店级联删书
.nullable()
})
o2m永远通过mappedBy指向对方某个 m2o/o2o 属性名,本身不产生列;joinColumns({ cascade: "DELETE" })声明级联:删书店时删掉它的书(级联方向由拥有方决定);o2m(...).orderBy(...)给集合定默认排序,查询取books时自动带上。
多对多:中间表
m2m 的常见形态是用中间表连接两侧(示例数据已含 author / book_author_mapping 表与数据,此例可运行,完整版本见工作区):
// 书 ↔ 作者:book_author_mapping 中间表(片段:完整可运行版见工作区)
const AUTHOR = model("Author", "id", class {
id = prop.i64()
name = prop.str(50)
books = prop.m2m(BOOK).mappedBy("authors") // 反向:不必成对声明,方便时再加
})
const BOOK = model("Book", "id", class {
id = prop.i64()
name = prop.str(50)
// 拥有侧:声明中间表与两侧的外键列
authors = prop.m2m(AUTHOR).joinTable({
name: "book_author_mapping",
joinThisColumns: ["book_id"], // 中间表里指向"本侧"的列
joinTargetColumns: ["author_id"], // 中间表里指向"对侧"的列
})
})
- 默认命名策略下中间表名、列名可省略(自动推导);
- 反向
m2m(...).mappedBy("authors")与 o2m 同理,不建任何列; - ⚠️ 已知限制(0.0.10):m2m 集合的子视图取 embedded 子路径(如
c.author.name.lastName)会触发 DTO 构造错误——先取整个 embedded(c.author.name)或标量;随上游修复跟进。
多对多带载荷:joinEntity
中间表还想带自己的字段(如选课成绩、关联时间)?用显式中间实体:
const COURSE = model("Course", "id", class {
id = prop.i64()
name = prop.str(50)
// students 经由 LearningLink 实体连接,成绩等字段在链上
students = prop.m2m(STUDENT).joinEntity({
model: LEARNING_LINK, // 中间实体(自带 id/score/student/course)
joinThisProp: "student", // 中间实体指向本侧的 m2o 属性
joinTargetProp: "course", // 中间实体指向对侧的 m2o 属性
})
})
此时 LearningLink 是一个普通实体:既能按关联用,也能直接查它的 score 等载荷字段。
一对一
o2o 语义与 m2o 相同,只是外键加了唯一约束(一侧只能有一个)。涉及它时请以当前版本 API 为准。
不对称重申
m2o 与 o2m 不需要成对出现(「快速上手 → 视图」讲过):外键建在哪张表,关联就在哪个模型声明;反向只是便利视角。m2m 同理——只在真正需要"从另一侧出发"时再补 mappedBy。
下一步
关联之外,模型体系还有两块大内容:继承与多态(同一个概念多种形态)与复合值(一列变成一组列)。先看继承。