Skip to content

Has Many 关联

介绍

Has Many 关联表示一个模型拥有另一个模型的多个实例。这是一种一对多的关联关系,常用于表示父子关系或分类关系。

定义 Has Many 关联

使用 @HasMany 装饰器来定义一对多关联:

ts
import { Table, Column, PrimaryKey, HasMany, BelongsTo, ColumnType } from '@ibestservices/ibest-orm';

@Table()
class User {
  @PrimaryKey()
  id?: number;

  @Column()
  name!: string;

  @Column()
  email?: string;

  // 一对多关联:一个用户有多篇文章
  @HasMany(() => Article, { foreignKey: 'user_id' })
  articles?: Article[];
}

@Table()
class Article {
  @PrimaryKey()
  id?: number;

  @Column()
  title!: string;

  @Column()
  content?: string;

  @Column({ name: 'user_id', type: ColumnType.INTEGER })
  userId?: number;

  // 多对一关联:文章属于一个用户
  @BelongsTo(() => User, { foreignKey: 'userId' })
  author?: User;
}

配置参数

target(第一个参数)

  • 类型: () => ClassClass
  • 描述: 目标模型类或返回目标模型类的工厂函数
  • 必需: 是
  • 示例: () => Article

options(第二个参数)

foreignKey

  • 类型: string
  • 描述: 目标表中的外键字段名
  • 默认值: ${当前表名}_id(自动推断)
  • 示例: 'user_id'

localKey

  • 类型: string
  • 描述: 当前表中的本地键字段名(通常是主键)
  • 默认值: 'id'

cascade

  • 类型: CascadeType[]
  • 描述: 级联操作类型
  • 可选值: [CascadeType.Delete]
  • 示例: 删除用户时自动删除其所有文章

lazy

  • 类型: boolean
  • 描述: 是否延迟加载
  • 默认值: true

查询 Has Many 关联

预加载关联数据

ts
import { getORM } from '@ibestservices/ibest-orm';

const orm = getORM();

// 查询用户及其所有文章
const users = orm.query(User)
  .with('articles')
  .find();

console.log('用户及文章:', users);
// 输出:
// [
//   {
//     id: 1,
//     name: "张三",
//     email: "zhangsan@example.com",
//     articles: [
//       { id: 1, title: "TypeScript入门指南", userId: 1 },
//       { id: 2, title: "React最佳实践", userId: 1 }
//     ]
//   }
// ]

查询单个用户的文章

ts
// 查询指定用户及其文章
const user = orm.query(User)
  .with('articles')
  .where({ id: 1 })
  .first();

console.log(`用户 ${user?.name} 共有 ${user?.articles?.length || 0} 篇文章`);

嵌套预加载

ts
// 查询用户、文章及文章的评论(需要 Article 定义 comments 关联)
const users = orm.query(User)
  .with('articles.comments')
  .find();

完整示例

ts
import {
  Table, Column, PrimaryKey, HasMany, BelongsTo,
  initORMWithMemory, getORM, ColumnType
} from '@ibestservices/ibest-orm';

// 定义模型
@Table()
class Author {
  @PrimaryKey()
  id?: number;

  @Column()
  name!: string;

  @HasMany(() => Book, { foreignKey: 'author_id' })
  books?: Book[];
}

@Table()
class Book {
  @PrimaryKey()
  id?: number;

  @Column()
  title!: string;

  @Column({ name: 'author_id', type: ColumnType.INTEGER })
  authorId?: number;

  @BelongsTo(() => Author, { foreignKey: 'authorId' })
  author?: Author;
}

// 初始化
const orm = initORMWithMemory();
orm.migrate(Author, Book);

// 创建数据
const author = new Author();
author.name = '鲁迅';
const authorId = orm.insert(author);

const book1 = new Book();
book1.title = '呐喊';
book1.authorId = authorId;
orm.insert(book1);

const book2 = new Book();
book2.title = '彷徨';
book2.authorId = authorId;
orm.insert(book2);

// 查询作者及其书籍
const authors = orm.query(Author).with('books').find();
console.log(authors[0]?.books); // [{ title: '呐喊' }, { title: '彷徨' }]

// 查询书籍及其作者
const books = orm.query(Book).with('author').find();
console.log(books[0]?.author?.name); // '鲁迅'

注意事项

提示

  • Has Many 关联返回数组,即使没有关联数据也会返回空数组 []
  • 预加载查询会执行两次 SQL:先查主表,再查关联表
  • 使用级联删除时要谨慎,确保业务逻辑正确

注意

  • 大量关联数据可能影响查询性能
  • 删除主记录前要考虑关联数据的处理策略
  • 外键字段名需要与数据库列名一致(使用 snake_case)