我正在参加「掘金·启航计划」
前言
存在关联关系的实体之间,在查询一个实体时,加载另一个实体的方式有两种,Eager 和 Lazy,分别是立即加载和延迟加载/懒加载。所谓的加载,就是指查询数据库的这个动作。
本文在 Nest 程序中,演示这两种加载方式的用法和区别。
准备两个实体
TypeORM 可以方便为实体设置一对一,一对多/多对一,多对多的关系,使用 @OneToOne()
、@OneToMany()/@ManyToOne
、@ManyToMany()
装饰器即可。
作者和文章是典型的一对多的关系,本文以这个一对多的关系来进行演示。
先来定义这两个实体。Author 实体如下:
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
import { Article } from './article.entity';
@Entity({ name: 'author' })
export class Author {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
avatar: string;
// 作者和文章是一对多的关系
@OneToMany(() => Article, (articles) => articles.author, {
cascade: true,
})
articles: Article[];
}
Article 实体如下:
import {
Column,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { Author } from './author.entity';
@Entity({ name: 'article' })
export class Article {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
banner: string;
// 文章和作者是多对一的关系
@ManyToOne(() => Author, (author) => author.articles)
@JoinColumn({ name: 'author_id' })
author: Author;
}
eager
Eager
立即加载,当查询一个实体时,所关联的实体会立即从数据库中加载。此模式的开启方式有两种。
使用 relations
当使用 find
、findOne
等方法查询实体时,如果通过 relations
属性指定了关联实体,则指定的实体就是 eager 加载。
Author 实体中声明的关系:
@OneToMany(() => Article, (articles) => articles.author, {
cascade: true
})
articles: Article[];
查询作者实体的方法:
async findAll() {
return this.authorRepository.findOne({
where: {
id: 1,
},
relations: {
articles: true,
},
});
}
此时生成的 SQL 如下,会生成一个左连接的连接查询,当查询 author 表时会查询 article 表:
SELECT `Author`.`id` AS `Author_id`, `Author`.`name` AS `Author_name`, `Author`.`avatar` AS `Author_avatar`, `Author__Author_articles`.`id` AS `Author__Author_articles_id`, `Author__Author_articles`.`title` AS `Author__Author_articles_title`, `Author__Author_articles`.`banner` AS `Author__Author_articles_banner`, `Author__Author_articles`.`author_id` AS `Author__Author_articles_author_id`
FROM `author` `Author`
LEFT JOIN `article` `Author__Author_articles`
ON `Author__Author_articles`.`author_id`=`Author`.`id` WHERE ( (`Author`.`id` = ?) ) AND ( `Author`.`id` IN (1) )
设置 eager
使用 @OneToMany()
等装饰器设置关系时,可通过 eager:true
开启,此时查询实体,无论是否指定 relations ,设置了关联关系的实体都是 eager 加载。
Author 实体中声明关系时开启 eager:
@OneToMany(() => Article, (articles) => articles.author, {
cascade: true,
eager: true
})
articles: Article[]
查询方法,不需要指定 relations:
async findAll() {
return this.authorRepository.findOne({
where: {
id: 1
},
});
}
生成的 SQL 如下,和上面的效果相同,也是生成一个左连接 article 表的查询:
lazy
Lazy
懒加载,当查询一个实体时,所关联的实体不会马上从数据库中加载,当访问到时,才去执行加载。懒加载的作用就类似网页中的图片懒加载,路由组件的懒加载,等用到时,才去加载,目的是提高性能。但实际来看,用的并不多。
TypeORM 中使用 lazy
加载也有两种方式。
使用 Promise
方式一是需要将关联实体的类型设为 Promise。Author 实体的代码:
@OneToMany(() => Article, (articles) => articles.author, {
cascade: true,
})
articles: Promise<Article[]>;
查询时,如果没有没有访问到关联实体的属性(author.articles)时,就不会去查询 Article 实体:
async findAll() {
return await this.authorRepository.findOne({
where: { id: 1 }
});
}
此时生成的 SQL 语句只查询了 author 表:
并且返回给前端的数据,也只有Author 实体:
当访问 author.articles
,也就是使用到 Author 实体关联的 Article 实体时,才会去查询 article 表。查询方法如下:
async findAll() {
const author = await this.authorRepository.findOne({
where: { id: 1 }
});
// 访问到关联的 Article 实体,才会去查询 article 表
console.log(await author.articles);
return author;
}
生成的 SQL 如下,可以看到执行了两次查询:
query: SELECT `Author`.`id` AS `Author_id`, `Author`.`name` AS `Author_name`, `Author`.`avatar` AS `Author_avatar` FROM `author` `Author` WHERE (`Author`.`id` = ?) LIMIT 1
query: SELECT `articles`.`id` AS `articles_id`, `articles`.`title` AS `articles_title`, `articles`.`banner` AS `articles_banner`, `articles`.`author_id` AS `articles_author_id` FROM `article` `articles` WHERE `articles`.`author_id` IN (?)
返回给浏览器的数据,也包含了 article 表的数据:
设置 lazy
方式二是在设置关联关系时,设置 lazy 属性为 true。Author 实体如下:
@OneToMany(() => Article, (articles) => articles.author, {
cascade: true,
lazy: true,
})
articles: Article[];
此时查询的效果,和上面那种方式是一致的,都是懒加载。
需要注意的是,如果设置了 lazy
,又在查询实体时使用了 relations
,那么此时的查询依然是 eager
的。
总结
实际上,TypeORM 文档将 eager 和 lazy 定义为关联实体之间的关系。Eager
关系是在查询完实体之后,立即查询所关联的实体,得到一个完整的数据。而 lazy
关系是当属性被访问到时才去加载相应实体,也就是才去执行相应的 SQL 查询语句。
大部分场景下,当我们需要查询关联实体的数据时,都会通过 relations
属性去指定,而非设置 eager
,这样更灵活一些。而 lazy
关系,TypeORM 文档也指出了,延迟加载是非标准技术,也是实验性质的,实际上很少使用。
原文链接:https://juejin.cn/post/7228134521291571255 作者:昆吾kw