实体关系
关系用于表达实体之间的关联。Aiao RxDB 支持四种关系:
- ONE_TO_ONE:一对一
- ONE_TO_MANY:一对多
- MANY_TO_ONE:多对一
- MANY_TO_MANY:多对多
关系会自动生成以下能力:
- 外键字段:对于
ONE_TO_ONE/MANY_TO_ONE,在当前实体上生成nameId外键(如ownerId) - 响应式属性:在实例上注入
name$可观察对象(详见下文“关系属性访问”)
一对一关系 (One-to-One)
一对一关系表示两个实体之间的直接对应关系,例如用户和身份证的关系。
在这个关系中:
- 一个用户有 0-1 个身份证(可选关系)
- 一个身份证有且只有 1 个用户(必需关系)
import { Entity, EntityBase, PropertyType, RelationKind } from '@aiao/rxdb';
@Entity({
name: 'User',
displayName: '用户表',
properties: [{ name: 'name', type: PropertyType.string, displayName: '姓名' }],
relations: [
{
name: 'idCard', // 关系属性名
kind: RelationKind.ONE_TO_ONE, // 关系类型:一对一
mappedEntity: 'IdCard', // 关联的实体
nullable: true // 是否允许 null(默认为 false)
}
]
})
export class User extends EntityBase {}
@Entity({
name: 'IdCard',
displayName: '身份证',
properties: [
{
name: 'code',
type: PropertyType.string,
displayName: '身份证号码',
unique: true
}
],
relations: [
{
name: 'owner', // 关系属性名
kind: RelationKind.ONE_TO_ONE, // 关系类型:一对一
mappedEntity: 'User', // 关联的实体
nullable: false // 是否允许 null(默认为 false)
}
]
})
export class IdCard extends EntityBase {}
一对一关系选项
核心选项:
- name: 关系属性名(小写开头)
- kind: 固定为
RelationKind.ONE_TO_ONE - mappedEntity: 关联实体名(可跨 namespace,配合
mappedNamespace) - mappedNamespace?: 关联实体命名空间,默认
public - nullable?: 外键可空(是否允许“无对应”)
- unique?: 是否唯一(通常一对一应为唯一)
- sortable?: 是否可排序
一对一的相关业务
- 删除用户时,关联的身份证也应该被删除
- 查询用户时,可以获取身份证信息
- 可以查询没有身份证的用户
- 可以查询身份证号码为特定值的用户
- 可以删除用户的身份证,不影响用户
一对多关系 (One-to-Many)
一对多关系表示一个实体可以关联多个另一类型的实体,而后者只能关联到一个前者。
电商系统示例:用户、订单、商品
在这个关系中:
- 一个用户可以有 0-N 个订单(一对多)
- 一个订单必须属于 1 个用户(多对一)
- 一个订单可以有 1-N 个订单项目(一对多)
- 一个订单项目必须属于 1 个订单(多对一)
import { Entity, EntityBase, PropertyType, RelationKind } from '@aiao/rxdb';
@Entity({
name: 'User',
displayName: '用户表',
properties: [{ name: 'name', type: PropertyType.string, displayName: '姓名' }],
relations: [
{
name: 'orders',
kind: RelationKind.ONE_TO_MANY,
mappedEntity: 'Order',
mappedProperty: 'owner'
}
]
})
class User extends EntityBase {}
@Entity({
name: 'Order',
displayName: '订单',
properties: [
{ name: 'orderNo', type: PropertyType.string, unique: true, displayName: '订单号' },
{ name: 'amount', type: PropertyType.number, displayName: '订单总金额' }
],
relations: [
{
name: 'owner', // 关系属性名
kind: RelationKind.MANY_TO_ONE, // 关系类型:多对一
mappedEntity: 'User' // 关联的实体
},
{
name: 'items', // 关系属性名
kind: RelationKind.ONE_TO_MANY, // 关系类型:一对多
mappedEntity: 'OrderItem', // 关联的实体
mappedProperty: 'order' // 对方实体中的关系属性名
}
]
})
export class Order extends EntityBase {}
@Entity({
name: 'OrderItem',
displayName: '订单项目',
properties: [
{ name: 'productName', type: PropertyType.string, displayName: '商品名称' },
{ name: 'quantity', type: PropertyType.number, displayName: '数量' },
{ name: 'price', type: PropertyType.number, displayName: '单价' }
],
relations: [
{
name: 'order', // 关系属性名
kind: RelationKind.MANY_TO_ONE, // 关系类型:多对一
mappedEntity: 'Order' // 关联的实体
}
]
})
export class OrderItem extends EntityBase {}
一对多/多对一关系选项
ONE_TO_MANY(当前实体拥有集合)
- name: 关系属性名
- kind: 固定为
RelationKind.ONE_TO_MANY - mappedEntity: 关联实体名
- mappedProperty: 对方实体指向当前实体的关系名(多对一那一端的属性名)
- sortable?: 是否可排序
MANY_TO_ONE(当前实体指向另一个实体)
- name: 关系属性名
- kind: 固定为
RelationKind.MANY_TO_ONE - mappedEntity: 关联实体名
- mappedNamespace?: 关联实体命名空间
- nullable?: 是否可空
- unique?: 是否唯一
- sortable?: 是否可排序
多对多关系 (Many-to-Many)
- 学生与课程
在这个关系中:
- 一个学生可以选择 0-N 个课程
- 一个课程可以被 0-N 个学生选择
import { Entity, EntityBase, PropertyType, RelationKind } from '@aiao/rxdb';
@Entity({
name: 'Student',
displayName: '学生',
properties: [
{ name: 'name', type: PropertyType.string, displayName: '姓名' },
{ name: 'studentId', type: PropertyType.string, unique: true, displayName: '学号' }
],
relations: [
{
name: 'courses', // 关系属性名
kind: RelationKind.MANY_TO_MANY, // 关系类型:多对多
mappedEntity: 'Course', // 关联的实体
mappedProperty: 'students' // 对方实体中的关系属性名
}
]
})
export class Student extends EntityBase {}
@Entity({
name: 'Course',
displayName: '课程',
properties: [
{ name: 'title', type: PropertyType.string, displayName: '课程名称' },
{ name: 'code', type: PropertyType.string, unique: true, displayName: '课程代码' },
{ name: 'credits', type: PropertyType.number, displayName: '学分' }
],
relations: [
{
name: 'students', // 关系属性名
kind: RelationKind.MANY_TO_MANY, // 关系类型:多对多
mappedEntity: 'Student', // 关联的实体
mappedProperty: 'courses' // 对方实体中的关系属性名
}
]
})
export class Course extends EntityBase {}
多对多关系选项
核心选项:
- name: 关系属性名
- kind: 固定为
RelationKind.MANY_TO_MANY - mappedEntity: 关联实体名
- mappedProperty: 对方实体中的关系属性名
- sortable?: 是否可排序
注意:框架会自动生成连接表(命名规则:两个实体名按字母序组合为 A_B,命名空间按字母序组合为 ns1_ns2),并注入到运行时。无需手动维护。
关系属性访问(实例 API)
装饰器会在实体实例上注入响应式属性用于访问关系:
- 单值关系:
name$为RelationEntityObservable<T>- 读取:
await firstValueFrom(entity.owner$) - 设置:
entity.owner$.set(other);移除:entity.owner$.remove()
- 读取:
- 多值关系:
name$为RelationEntitiesObservable<T[]>- 读取:
await firstValueFrom(entity.items$);计数:entity.items$.count$ - 变更:
entity.items$.add(a, b)/entity.items$.remove(a)
- 读取:
此外,对于 ONE_TO_ONE/MANY_TO_ONE 会自动生成外键字段 nameId,可在初始化或更新时直接赋值(如 order.ownerId = user.id)。