Many To Many 关联
介绍
Many To Many 关联表示两个实体之间存在多对多的关系。这种关联需要通过中间表(也称为连接表或枢纽表)来维护关系,每个实体都可以拥有另一个实体的多个实例。
定义 Many To Many 关联
使用 @ManyToMany 装饰器来定义多对多关联:
ts
import { Table, Column, PrimaryKey, ManyToMany } from '@ibestservices/ibest-orm';
@Table()
class User {
@PrimaryKey()
id?: number;
@Column()
name!: string;
@Column()
email?: string;
@ManyToMany(() => Role, {
through: 'user_roles',
throughForeignKey: 'user_id',
throughOtherKey: 'role_id'
})
roles?: Role[];
}
@Table()
class Role {
@PrimaryKey()
id?: number;
@Column()
name!: string;
@Column()
description?: string;
@ManyToMany(() => User, {
through: 'user_roles',
throughForeignKey: 'role_id',
throughOtherKey: 'user_id'
})
users?: User[];
}配置参数
target
- 类型:
() => Class - 描述: 目标模型类的工厂函数
- 必需: 是
through
- 类型:
string - 描述: 中间表的名称
- 必需: 是
- 示例:
'user_roles','article_tags'
throughForeignKey
- 类型:
string - 描述: 中间表中指向当前模型的外键字段名
- 必需: 是
- 示例:
'user_id','article_id'
throughOtherKey
- 类型:
string - 描述: 中间表中指向目标模型的外键字段名
- 必需: 是
- 示例:
'role_id','tag_id'
中间表
多对多关联需要一个中间表来存储关联关系。IBest ORM 会在 migrate() 时自动创建中间表:
typescript
// 调用 migrate 时会自动创建中间表
orm.migrate(User, Role);
// IBest ORM 会自动创建 user_roles 表,结构如下:
// CREATE TABLE IF NOT EXISTS user_roles (
// user_id INTEGER NOT NULL,
// role_id INTEGER NOT NULL,
// PRIMARY KEY (user_id, role_id)
// )自动表创建
调用 migrate() 时,IBest ORM 会自动扫描实体的 ManyToMany 关联配置,并创建对应的中间表:
- 两个外键字段(由
throughForeignKey和throughOtherKey指定) - 复合主键约束(确保关联唯一性)
查询 Many To Many 关联
预加载关联数据
ts
// 查询用户及其角色
const usersWithRoles = orm.query(User)
.with('roles')
.where({ id: 1 })
.find();
console.log('用户及角色:', usersWithRoles[0]);
// 输出:
// {
// id: 1,
// name: "张三",
// email: "zhangsan@example.com",
// roles: [
// {
// id: 1,
// name: "admin",
// description: "管理员"
// },
// {
// id: 2,
// name: "editor",
// description: "编辑"
// }
// ]
// }反向查询
ts
// 查询角色及其用户
const rolesWithUsers = orm.query(Role)
.with('users')
.where({ id: 1 })
.find();
console.log('角色及用户:', rolesWithUsers[0]);
// 输出:
// {
// id: 1,
// name: "admin",
// description: "管理员",
// users: [
// {
// id: 1,
// name: "张三",
// email: "zhangsan@example.com"
// },
// {
// id: 3,
// name: "王五",
// email: "wangwu@example.com"
// }
// ]
// }查询所有关联数据
ts
// 查询所有用户及其角色
const usersWithRoles = orm.query(User)
.with('roles')
.find();
usersWithRoles.forEach(user => {
const roleNames = user.roles?.map(role => role.name).join(', ') || '无角色';
console.log(`${user.name}: ${roleNames}`);
});注意事项
级联操作
ManyToMany 关联支持级联创建、更新和删除操作:
typescript
import { Table, Column, PrimaryKey, ManyToMany, CascadeType } from '@ibestservices/ibest-orm';
@Table()
class Article {
@PrimaryKey()
id?: number;
@Column()
title!: string;
@ManyToMany(() => Tag, {
through: 'article_tags',
throughForeignKey: 'article_id',
throughOtherKey: 'tag_id',
cascade: [CascadeType.Create, CascadeType.Update, CascadeType.Delete]
})
tags?: Tag[];
}
@Table()
class Tag {
@PrimaryKey()
id?: number;
@Column()
name!: string;
}
// 级联创建:自动创建文章、标签和中间表关联
const article = new Article();
article.title = 'TypeScript 入门';
article.tags = [
Object.assign(new Tag(), { name: '技术' }),
Object.assign(new Tag(), { name: '教程' })
];
orm.insertWithRelations(article);
// 级联更新:同步关联关系(删除旧关联,创建新关联)
article.tags = [
Object.assign(new Tag(), { name: '前端' }),
Object.assign(new Tag(), { name: 'JavaScript' })
];
orm.saveWithRelations(article);
// 级联删除:删除文章和中间表关联(不删除标签本身)
orm.deleteWithRelations(article);提示
- 预加载查询会执行多次 SQL:主表、关联表、中间表
- 关联数据为空数组
[]时表示没有关联记录 - 级联删除只会删除中间表的关联记录,不会删除关联的实体
注意
- 级联更新会先删除所有旧的中间表记录,再插入新的关联
- 大量关联数据可能影响查询性能
- 建议合理使用预加载来优化性能