TypeORM 的 Eager 和 Lazy 关系

我正在参加「掘金·启航计划」

前言

存在关联关系的实体之间,在查询一个实体时,加载另一个实体的方式有两种,EagerLazy,分别是立即加载和延迟加载/懒加载。所谓的加载,就是指查询数据库的这个动作。

本文在 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

当使用 findfindOne 等方法查询实体时,如果通过 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 表的查询:

TypeORM 的 Eager 和 Lazy 关系

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 表:

TypeORM 的 Eager 和 Lazy 关系

并且返回给前端的数据,也只有Author 实体:

TypeORM 的 Eager 和 Lazy 关系

当访问 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 1query: 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 表的数据:

TypeORM 的 Eager 和 Lazy 关系

设置 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

(0)
上一篇 2023年5月2日 上午10:21
下一篇 2023年5月2日 上午10:31

相关推荐

发表回复

登录后才能评论