复合值 embedded
当一组列总是作为一个整体出现(姓名、地址、坐标),可以用 prop.embedded 把它们打包成一个值对象:
// ① 作者:name 是复合值(两列打包)
const AUTHOR = model("Author", "id", class {
id = prop.i64()
name = prop.embedded({
firstName: prop.str(50),
lastName: prop.str(50),
})
})
表结构上仍然是普通列,而且成员名直接作列名(不拼接父属性路径):name.firstName → FIRST_NAME、name.lastName → LAST_NAME。模型侧是一个结构,查询、取形、排序都按路径访问。
复合主键
主键同样可以嵌入多列,甚至嵌套:
const ORDER = model("Order", "id", class {
// id 是一个复合结构:x 与嵌套的 y{a,b}——共三列做主键(列名 X / Y_A / Y_B)
id = prop.embedded({
x: prop.i32(),
y: prop.embedded({
a: prop.i16(),
b: prop.i16(),
}),
})
name = prop.str(10)
})
路径引用
复合值内部通过子路径访问,写法和属性访问一致:
// ② 复合值的路径:取名、按姓排序(片段:需在查询上下文中使用)
q.where(author.name.firstName.eq("Tao")) // WHERE NAME_FIRST_NAME = ?
q.orderBy(author.name.lastName) // ORDER BY NAME_LAST_NAME
// 复合主键的子路径:排序、查询都可以(下例取 Tag 的复合 id)
q.orderBy(tag.id.low, tag.id.high) // 复合 id 的 low/high 分量
多列外键
关联指向复合主键时,外键就是一组列。joinColumns 的 referencedSubPath 把本侧列映射到对方复合键的分量:
const ORDER_ITEM = model("OrderItem", "id", class {
id = prop.i64()
order = prop.m2o(ORDER).joinColumns({
columns: [
{ columnName: "order_x", referencedSubPath: "x" },
{ columnName: "order_y_a", referencedSubPath: "y.a" },
{ columnName: "order_y_b", referencedSubPath: "y.b" },
],
cascade: "DELETE",
})
})
referencedSubPath 用点号指到对方复合键的具体分量;m2m 中间表(joinThis/referenceTarget 的 columns)同理支持复合键。
定位:复用字段用 embedded,不要用继承
单纯想把一组列打包复用(姓名、地址、坐标)时,用 embedded 而不是继承——继承会引入单表继承与判别列(见「继承与多态」),为了省重复代码而继承得不偿失。embedded 只是结构打包,列仍是普通列,不会改变表形态。
下一步
列、关联、形态都齐了,模型还有一个经常被高估复杂度的能力:公式与计算属性——把 SQL 表达式或 TS 计算挂到模型的普通属性上。