跳转到主要内容

处理关系

RelationQueryBuilder 是一种特殊类型的 QueryBuilder,它允许您处理关系。 使用它,您可以在数据库中将实体彼此绑定,而无需加载任何实体,或者可以轻松加载相关实体。

例如,我们有一个 Post 实体,它与 Category 之间有一个多对多的关系,称为 categories。 让我们将一个新的分类添加到这个多对多关系中:

await dataSource
.createQueryBuilder()
.relation(Post, "categories")
.of(post)
.add(category)

这段代码等价于以下操作:

const postRepository = dataSource.manager.getRepository(Post)
const post = await postRepository.findOne({
where: {
id: 1,
},
relations: {
categories: true,
},
})
post.categories.push(category)
await postRepository.save(post)

但它更高效,因为它执行的操作数量较少,并且在数据库中绑定实体,而不是调用庞大的 save 方法。

此外,使用这种方法的另一个好处是,在将实体推送到关系之前,您无需加载每个相关实体。 例如,如果在单个帖子中有一万个类别,将新帖子添加到此列表可能会成为问题,因为标准方法是加载带有一万个类别的帖子, 然后推送一个新的类别,并保存。这会导致非常重大的性能开销,基本上在实际环境中无法应用。 但是,使用 RelationQueryBuilder 可以解决这个问题。

此外,在 "绑定" 事物时,没有必要真正使用实体,因为您可以使用实体的 ID。 例如,让我们将具有 ID = 3 的类别添加到 ID = 1 的帖子中:

await dataSource.createQueryBuilder().relation(Post, "categories").of(1).add(3)

如果您使用的是复合主键,则必须将其作为 ID 映射传递,例如:

await dataSource
.createQueryBuilder()
.relation(Post, "categories")
.of({ firstPostId: 1, secondPostId: 3 })
.add({ firstCategoryId: 2, secondCategoryId: 4 })

您可以使用与添加相同的方式删除实体:

// 这段代码从给定的帖子中移除一个类别
await dataSource
.createQueryBuilder()
.relation(Post, "categories")
.of(post) // 也可以只使用帖子的 ID
.remove(category) // 也可以只使用类别的 ID

添加和删除相关实体适用于 多对多一对多 关系。 对于 一对一多对一 关系,请使用 set

// 这段代码设置给定帖子的类别
await dataSource
.createQueryBuilder()
.relation(Post, "categories")
.of(post) // 也可以只使用帖子的 ID
.set(category) // 也可以只使用类别的 ID

如果要取消关系(将其设置为 null),只需将 null 传递给 set 方法:

// 这段代码取消给定帖子的类别
await dataSource
.createQueryBuilder()
.relation(Post, "categories")
.of(post) // 也可以只使用帖子的 ID
.set(null)

除了更新关系,关系查询构建器还允许您加载关系实体。 例如,假设在 Post 实体中,我们有一个多对多的 categories 关系和一个多对一的 user 关系, 要加载这些关系,可以使用以下代码:

const post = await dataSource.manager.findOneBy(Post, {
id: 1,
})

post.categories = await dataSource
.createQueryBuilder()
.relation(Post, "categories")
.of(post) // 也可以只使用帖子的 ID
.loadMany()

post.author = await dataSource
.createQueryBuilder()
.relation(Post, "user")
.of(post) // 也可以只使用帖子的 ID
.loadOne()