Many To Many 关联
介绍
Many To Many
关联表示两个模型之间存在多对多的关系。这种关联需要通过中间表(也称为连接表或枢纽表)来维护关系,每个模型都可以拥有另一个模型的多个实例。
定义 Many To Many 关联
使用 @ManyToMany
装饰器来定义多对多关联:
ts
import { Table, Field, FieldType, Model, ManyToMany } from '@ibestservices/ibest-orm';
@Table({ name: 'users' })
class User extends Model {
@Field({ type: FieldType.TEXT, tag: ['notNull'] })
name?: string;
@Field({ type: FieldType.TEXT })
email?: string;
@ManyToMany({
target: () => Role,
through: 'user_roles',
throughForeignKey: 'user_id',
throughOtherKey: 'role_id'
})
roles?: Role[];
constructor(name: string, email: string) {
super();
this.name = name;
this.email = email;
}
}
@Table({ name: 'roles' })
class Role extends Model {
@Field({ type: FieldType.TEXT, tag: ['notNull'] })
name?: string;
@Field({ type: FieldType.TEXT })
description?: string;
@ManyToMany({
target: () => User,
through: 'user_roles',
throughForeignKey: 'role_id',
throughOtherKey: 'user_id'
})
users?: User[];
constructor(name: string, description?: string) {
super();
this.name = name;
this.description = description;
}
}
配置参数
target
- 类型:
() => Class
- 描述: 目标模型类的工厂函数
- 必需: 是
through
- 类型:
string
- 描述: 中间表的名称
- 必需: 是
- 示例:
'user_roles'
,'article_tags'
throughForeignKey
- 类型:
string
- 描述: 中间表中指向当前模型的外键字段名
- 必需: 是
- 示例:
'user_id'
,'article_id'
throughOtherKey
- 类型:
string
- 描述: 中间表中指向目标模型的外键字段名
- 必需: 是
- 示例:
'role_id'
,'tag_id'
中间表
多对多关联需要一个中间表来存储关联关系。IBest ORM 会自动根据关联配置创建中间表,无需手动创建:
typescript
// IBest ORM 会自动创建 user_roles 表,包含:
// - id (主键)
// - user_id (外键,关联到 users 表)
// - role_id (外键,关联到 roles 表)
// - created_at (创建时间)
// - 唯一约束 (user_id, role_id)
// - 外键约束和级联删除
自动表创建
IBest ORM 会在首次使用多对多关联时自动创建中间表,包括:
- 主键字段
- 外键字段和约束
- 唯一性约束
- 创建时间字段
- 级联删除规则
查询 Many To Many 关联
预加载关联数据
ts
// 查询用户及其角色
const userWithRoles = await this.orm.Session(User)
.With('roles')
.Where('id', 1)
.FirstWithRelations();
console.log('用户及角色:', userWithRoles);
// 输出:
// {
// id: 1,
// name: "张三",
// email: "zhangsan@example.com",
// roles: [
// {
// id: 1,
// name: "admin",
// description: "管理员"
// },
// {
// id: 2,
// name: "editor",
// description: "编辑"
// }
// ]
// }
反向查询
ts
// 查询角色及其用户
const roleWithUsers = await this.orm.Session(Role)
.With('users')
.Where('id', 1)
.FirstWithRelations();
console.log('角色及用户:', roleWithUsers);
// 输出:
// {
// id: 1,
// name: "admin",
// description: "管理员",
// users: [
// {
// id: 1,
// name: "张三",
// email: "zhangsan@example.com"
// },
// {
// id: 3,
// name: "王五",
// email: "wangwu@example.com"
// }
// ]
// }
查询所有关联数据
ts
// 查询所有用户及其角色
const usersWithRoles = await this.orm.Session(User)
.With('roles')
.FindWithRelations();
usersWithRoles.forEach(user => {
const roleNames = user.roles?.map(role => role.name).join(', ') || '无角色';
console.log(`${user.name}: ${roleNames}`);
});
注意事项
提示
- IBest ORM 会自动创建和管理中间表
- 中间表自动包含主键、外键约束和唯一性约束
- 预加载查询会执行多次 SQL:主表、关联表、中间表
- 关联数据为空数组
[]
时表示没有关联记录 - 支持级联操作自动管理关联关系
注意
- 中间表的外键字段会自动建立索引
- 使用级联删除时会自动清理中间表的关联数据
- 大量关联数据可能影响查询性能
- 建议合理使用预加载和延迟加载来优化性能