跳到主要内容

实体关系

关系用于表达实体之间的关联。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)。