基于 NestJS 操作 TypeORM 中的一对多 OneToMany
前言
TypeORM 是一个在 TypeScript 和 JavaScript (ES7, ES6, ES5) 中提供了许多开箱即用特性的 ORM,它能够改善我们处理数据库操作的效率与可维护性。在复杂的应用开发过程中,数据间的关系处理显得尤其重要。今天我们将在 NestJS 中探索 TypeORM 的 OneToMany(一对多)关系,这是一种常见且强大的数据模型关系。在建立应用程序时,我们通常需要处理大量的增删查改(CRUD)操作,而 OneToMany 关系的巧妙应用能够让这些操作变得更为简洁、优雅。
使用步骤
一、安装依赖
首先需要在我们的 NestJS 应用中安装并设置 TypeORM。在你的项目根目录下执行以下命令:
shellnpm install --save @nestjs/typeorm typeorm
二、模块初始化
在项目的 app.module.ts
文件中进行 TypeORM 的设置。
typescriptimport { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { Author } from './author.entity'; import { Post } from './post.entity'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'postgres', host: 'localhost', port: 5432, username: 'test', password: 'test', database: 'test', entities: [Author, Post], synchronize: true, }), ], controllers: [AppController], providers: [AppService], }) export class AppModule {}
三、建立一对多的实体关系
我们创建**作者
和 文章
这两个实体。先建立 作者实体
,再设定 文章实体
**。
Author
typescriptimport {Entity, Column, OneToMany, PrimaryGeneratedColumn} from 'typeorm'; import { Post } from './post.entity'; @Entity() export class Author { @PrimaryGeneratedColumn() id: number; @Column() name: string; @OneToMany((type) => Post, (post) => post.author) posts: Post[]; }
Post
typescriptimport {Entity, PrimaryGeneratedColumn, Column, ManyToOne} from 'typeorm'; import { Author } from './author.entity'; @Entity() export class Post { @PrimaryGeneratedColumn() id: number; @Column() title: string; @ManyToOne((type) => Author, (author) => author.posts) author: Author; }
四、实现对应的服务层
为了创建作者和他的文章,我们需要创建一个服务。服务通过依赖注入来定义在各个模块之间的关系,你还需要记住的一点是:在 NestJS 中,你需要使用 Repository 来操作数据库。而 InjectRepository()
装饰器会帮助我们自动创建并注册这个 Repository。
Author Service
typescriptimport { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Author } from './author.entity'; import { Post } from './post.entity'; @Injectable() export class AuthorService { constructor( @InjectRepository(Author) private authorsRepository: Repository<Author>, @InjectRepository(Post) private postsRepository: Repository<Post>, ) {} async createAuthorWithPosts() { const author = this.authorsRepository.create({ name: 'John Doe' }); const post1 = this.postsRepository.create({ title: 'Post #1', author, }); const post2 = this.postsRepository.create({ title: 'Post #2', author, }); author.posts = [post1, post2]; await this.authorsRepository.save(author); // ... } }
五、一对多的增删改查操作
-
新增 Create
在 AuthorService 中,我们已经创建了
createAuthorWithPosts
方法,可以同时创建作者和对应的文章:typescriptasync createAuthorWithPosts() { const author = this.authorsRepository.create({ name: 'John Doe' }); const post1 = this.postsRepository.create({ title: 'Post #1', author, }); const post2 = this.postsRepository.create({ title: 'Post #2', author, }); author.posts = [post1, post2]; await this.authorsRepository.save(author); }
-
删除 Remove
删除某一篇文章,可以通过
remove
方法来达成:typescriptasync deletePost(postId: number) { let toDelete = await this.postsRepository.findOne(postId); let deleted = false; if (toDelete) { await this.postsRepository.remove(toDelete); deleted = true; } return { deleted }; }
-
修改 Update
假设我们要更新某一篇文章的信息,可以通过
update
方法来操作:typescriptasync updatePost(postId: number, updatedTitle: string) { let toUpdate = await this.postsRepository.findOne(postId); let updated = false; if (toUpdate) { toUpdate.title = updatedTitle; await this.postsRepository.save(toUpdate); updated = true } return { updated }; }
-
查询 Search
例如我们想要查找一个作者所有的文章,可以创建如下方法:
typescriptasync findPostsByAuthor(authorId: number) { return this.postsRepository.createQueryBuilder("post") .innerJoinAndSelect("post.author", "author") .where("author.id = :authorId", { authorId }) .getMany(); }
-
列表查询
假设我们想要查询所有的作者列表,可以通过
find
方法实现:typescriptasync findAllAuthors() { return await this.authorsRepository.find(); }
进阶操作
一、分页查询
当你的数据太多,你可能需要进行分页查询。我们可以使用 take
和 skip
方法进行分页查询。
下面这个方法展示了如何获取第二页的作者,每页有5个作者:
typescriptasync findAuthorsPagination(page: number) { return await this.authorsRepository.find({ take: 5, skip: 5 * (page - 1), }); }
在这个方法中, take
表示要获取的条数,skip
表示要跳过的条数。
二、关联查询
在我们有了OneToMany的关系后, 假设我们需要获取包含了一个作者的所有文章的信息, 那我们就需要做一个关联查询:
typescriptasync findAuthorAndPosts(authorId: number) { return this.authorsRepository.findOne(authorId, { relations: ["posts"] }); }
这个 findOne
方法会返回一个 Author
对象,该对象包含一个 Post
对象的数组,表示该作者写的所有文章。
总结
在使用 NestJS 和 TypeORM 进行开发时,我们已经掌握了 OneToMany(一对多)关系的设定、如何进行基础的 CRUD(增删查改)操作,还了解了列表查询、分页查询及关联查询的实现方式。最后,不要忘记,TypeORM 的 OneToMany 关系默认情况下是懒加载的,使得我们在需要时才去加载关联数据,提升了程序的运行效率。借助 NestJS 和 TypeORM,我们能够轻松处理复杂的数据关联关系,提升开发效率。