跳到主要内容

急切关联和懒加载关联

急切关联

急切关联在每次从数据库加载实体时都会自动加载。 例如:

import { Entity, PrimaryGeneratedColumn, Column, ManyToMany } from "typeorm"
import { Question } from "./Question"

@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number

@Column()
name: string

@ManyToMany((type) => Question, (question) => question.categories)
questions: Question[]
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToMany,
JoinTable,
} from "typeorm"
import { Category } from "./Category"

@Entity()
export class Question {
@PrimaryGeneratedColumn()
id: number

@Column()
title: string

@Column()
text: string

@ManyToMany((type) => Category, (category) => category.questions, {
eager: true,
})
@JoinTable()
categories: Category[]
}

现在当你加载问题时,不需要使用 join 或指定想要加载的关联。 它们会自动加载:

const questionRepository = dataSource.getRepository(Question)

// questions 会自动加载其 categories
const questions = await questionRepository.find()

急切关联只在使用 find* 方法时有效。 如果使用 QueryBuilder,急切关联将被禁用,必须使用 leftJoinAndSelect 来加载关联。 急切关联只能用于关系的一方, 两边都使用 eager: true 是不允许的。

默认情况下,急切关联使用 LEFT JOIN 进行加载。如果关联同时标记为 nullable: false(并且它拥有连接列,即 ManyToOne 或拥有方的 OneToOne),TypeORM 会改用 INNER JOIN,这可以生成更高效的查询计划。

关联加载策略

默认情况下,急切关联通过向主查询添加 SQL JOIN 来加载("join" 策略)。如果你在使用嵌套 join 时加载了过多数据,可以切换到 "query" 策略,它通过单独的数据库查询来加载关联:

// 单次查询
const questions = await questionRepository.find({
relationLoadStrategy: "query",
})

// 或者为整个 DataSource 设置默认值
const dataSource = new DataSource({
// ...
relationLoadStrategy: "query",
})

你还可以使用 loadEagerRelations 来控制是否加载急切关联:

// 完全禁用急切关联加载
const questions = await questionRepository.find({
loadEagerRelations: false,
})

// 仅加载显式指定的关联,抑制嵌套的急切关联
const questions = await questionRepository.find({
relations: { categories: true },
loadEagerRelations: false,
})

懒加载关联

懒加载关联中的实体会在你访问它们时加载。 这类关联的类型必须是 Promise — 你将值存储在一个 Promise 中, 加载时也会返回一个 Promise。示例:

import { Entity, PrimaryGeneratedColumn, Column, ManyToMany } from "typeorm"
import { Question } from "./Question"

@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number

@Column()
name: string

@ManyToMany((type) => Question, (question) => question.categories)
questions: Promise<Question[]>
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToMany,
JoinTable,
} from "typeorm"
import { Category } from "./Category"

@Entity()
export class Question {
@PrimaryGeneratedColumn()
id: number

@Column()
title: string

@Column()
text: string

@ManyToMany((type) => Category, (category) => category.questions)
@JoinTable()
categories: Promise<Category[]>
}

categories 是一个 Promise。这意味着它是懒加载的,只能存储一个包含值的 Promise。 下面是保存这类关联的示例:

const category1 = new Category()
category1.name = "animals"
await dataSource.manager.save(category1)

const category2 = new Category()
category2.name = "zoo"
await dataSource.manager.save(category2)

const question = new Question()
question.categories = Promise.resolve([category1, category2])
await dataSource.manager.save(question)

下面是如何加载懒加载关联内部对象的示例:

const [question] = await dataSource.getRepository(Question).find()
const categories = await question.categories
// 现在变量 "categories" 中包含了该 question 的所有类别

注意:如果你来自其他语言(如 Java、PHP 等),并习惯到处使用懒加载关联——请小心。 那些语言是同步的,懒加载是以不同方式实现的,不依赖 Promise。 在 JavaScript 和 Node.JS 中,如果想实现懒加载关联,必须使用 Promise。 这是一种非标准技术,在 TypeORM 中被视为实验性功能。