Nest基本概念 - 数据库集成

7/12/2023 NodejsNestjs

在本章中,我们将学习如何在 NestJS 应用程序中集成数据库。数据库是大多数应用程序必不可少的一部分,它用于存储和管理数据。NestJS 支持多种数据库的集成,包括关系型数据库和非关系型数据库。

# 4.1 安装数据库驱动

在集成数据库之前,我们需要先安装相应的数据库驱动。NestJS 支持许多数据库驱动,例如:

  • TypeORM:适用于关系型数据库的对象关系映射(ORM)库,支持 MySQL、-PostgreSQL、SQLite、MariaDB、SQL Server 等数据库。
  • Mongoose:适用于 MongoDB 的对象模型库。 这里我们以 TypeORM 为例来进行数据库集成。

首先,我们需要安装 TypeORM 和相关依赖:

npm install @nestjs/typeorm typeorm mysql
1
  • @nestjs/typeorm:NestJS 提供的 TypeORM 模块。
  • typeorm:TypeORM 库。
  • mysql:MySQL 数据库驱动(根据实际数据库选择对应的驱动)。

# 4.2 创建数据库连接

在使用 TypeORM 之前,我们需要先创建数据库连接。在 NestJS 中,我们使用 TypeOrmModule.forRoot() 方法来创建数据库连接。

打开 app.module.ts 文件,并添加以下代码:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql', // 数据库类型
      host: 'localhost', // 数据库主机地址
      port: 3306, // 数据库端口号
      username: 'your_username', // 数据库用户名
      password: 'your_password', // 数据库密码
      database: 'your_database', // 数据库名
      autoLoadEntities: true, // 自动加载实体
      synchronize: true, // 自动同步数据库结构,谨慎使用生产环境
    }),
  ],
})
export class AppModule {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

在上面的代码中,我们通过 TypeOrmModule.forRoot() 方法来创建一个 MySQL 数据库连接。你需要根据实际情况,修改 type、host、port、username、password 和 database 等参数来配置数据库连接。

注意:在生产环境中,不建议使用 synchronize: true,因为它会自动创建数据库表结构和索引,可能会导致数据丢失。在生产环境中,应该手动管理数据库结构。

# 4.3 创建实体

在 TypeORM 中,实体是映射到数据库表的 JavaScript 类。每个实体类代表数据库表中的一行数据。

我们先创建一个 Cat 实体,用于映射到数据库中的 cats 表。在 cats 表中,我们将包含 id、name 和 age 字段。

首先,在 src 目录下创建一个新的文件 cat.entity.ts,并添加以下代码:

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Cat {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  age: number;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

在上面的代码中,我们使用 @Entity() 装饰器来定义一个实体类 Cat。@PrimaryGeneratedColumn() 装饰器定义了主键字段 id,@Column() 装饰器定义了普通字段 name 和 age。

# 4.4 创建数据库仓库

在 TypeORM 中,数据库仓库用于对数据库表进行 CRUD(增删改查)操作。每个实体类都需要一个对应的数据库仓库。

我们创建一个 CatsRepository 数据库仓库来对 Cat 实体进行操作。

首先,在 src 目录下创建一个新的文件 cats.repository.ts,并添加以下代码:

import { EntityRepository, Repository } from 'typeorm';
import { Cat } from './cat.entity';

@EntityRepository(Cat)
export class CatsRepository extends Repository<Cat> {}
1
2
3
4
5

在上面的代码中,我们使用 @EntityRepository() 装饰器来定义一个数据库仓库类 CatsRepository,并传入 Cat 实体作为泛型参数。

# 4.5 使用数据库仓库

现在我们已经创建了数据库连接、实体和数据库仓库,可以在服务中使用数据库仓库来进行数据库操作了。

首先,我们在 cats.service.ts 中引入 CatsRepository:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { CatsRepository } from './cats.repository';
import { Cat } from './cat.entity';

@Injectable()
export class CatsService {
  constructor(
    @InjectRepository(CatsRepository)
    private catsRepository: CatsRepository,
  ) {}

  // 在这里可以使用 this.catsRepository 对数据库进行增删改查操作
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在上面的代码中,我们使用 @InjectRepository() 装饰器来注入 CatsRepository 作为 catsRepository 成员变量。

现在,CatsService 中就可以使用 this.catsRepository 对数据库进行增删改查操作了。

# 4.6 使用服务进行数据库操作

在上一节中,我们已经创建了 CatsService 服务,并注入了 CatsRepository。现在,我们可以在 CatsService 中使用 CatsRepository 来对数据库进行操作。

在 cats.service.ts 文件中,我们可以添加一些方法来实现对 Cat 实体的增删改查操作。

在 CatsService 中添加对 Cat 实体的增删改查操作:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { CatsRepository } from './cats.repository';
import { Cat } from './cat.entity';

@Injectable()
export class CatsService {
  constructor(
    @InjectRepository(CatsRepository)
    private catsRepository: CatsRepository,
  ) {}

  async findAll(): Promise<Cat[]> {
    return this.catsRepository.find();
  }

  async findById(id: number): Promise<Cat> {
    return this.catsRepository.findOne(id);
  }

  async create(cat: Cat): Promise<Cat> {
    return this.catsRepository.save(cat);
  }

  async update(id: number, updates: Partial<Cat>): Promise<Cat> {
    const cat = await this.findById(id);
    if (!cat) {
      throw new Error('Cat not found');
    }
    Object.assign(cat, updates);
    return this.catsRepository.save(cat);
  }

  async delete(id: number): Promise<void> {
    await this.catsRepository.delete(id);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

在上面的代码中,我们添加了 findAll 方法来查询所有的猫,findById 方法来根据猫的 ID 查询单个猫,create 方法来创建新的猫,update 方法来更新猫的信息,delete 方法来删除猫。

在这些方法中,我们直接使用了 catsRepository 对数据库进行操作。例如,

  • this.catsRepository.find() 查询所有的猫,
  • this.catsRepository.findOne(id) 根据猫的 ID 查询单个猫,
  • this.catsRepository.save(cat) 创建新的猫或更新猫的信息,
  • this.catsRepository.delete(id) 删除猫。

# 4.7 使用控制器调用服务方法

现在我们已经创建了数据库连接、实体、数据库仓库和服务,接下来我们需要使用控制器来调用服务方法。

在 cats.controller.ts 文件中,我们引入 CatsService 并添加以下代码:

import { Controller, Get, Post, Put, Delete, Body, Param } from '@nestjs/common';
import { CatsService } from './cats.service';
import { Cat } from './cat.entity';

@Controller('cats')
export class CatsController {
  constructor(private catsService: CatsService) {}

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }

  @Get(':id')
  async findById(@Param('id') id: number): Promise<Cat> {
    return this.catsService.findById(id);
  }

  @Post()
  async create(@Body() cat: Cat): Promise<Cat> {
    return this.catsService.create(cat);
  }

  @Put(':id')
  async update(@Param('id') id: number, @Body() updates: Partial<Cat>): Promise<Cat> {
    return this.catsService.update(id, updates);
  }

  @Delete(':id')
  async delete(@Param('id') id: number): Promise<void> {
    return this.catsService.delete(id);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

在上面的代码中,我们通过构造函数注入了 CatsService,并在控制器中使用 CatsService 的方法来实现对猫的增删改查操作。

这样,我们就完成了数据库集成的步骤。现在,当我们发送 GET、POST、PUT 和 DELETE 请求到 /cats 路径时,NestJS 会自动调用相应的控制器方法,并通过服务调用数据库仓库进行数据库操作。

# 4.8 额外的数据库操作

除了基本的增删改查操作,TypeORM 还提供了许多其他数据库操作功能,如使用查询构建器、使用原生 SQL 查询、使用事务等。在实际应用中,我们可以根据需求来选择使用这些功能。

例如,我们可以使用查询构建器来执行复杂的查询:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { CatsRepository } from './cats.repository';
import { Cat } from './cat.entity';

@Injectable()
export class CatsService {
  constructor(
    @InjectRepository(CatsRepository)
    private catsRepository: CatsRepository,
  ) {}

  async findByName(name: string): Promise<Cat[]> {
    return this.catsRepository
      .createQueryBuilder('cat')
      .where('cat.name = :name', { name })
      .getMany();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

在上面的代码中,我们使用查询构建器来执行一个名为 findByName 的查询,该查询会查找所有名字为给定参数 name 的猫。

TypeORM 还提供了许多其他高级特性,如数据迁移、数据库关系、联表查询等。在实际应用中,我们可以根据需求和项目的复杂程度来选择适合的数据库操作方式。

编辑时间: 7/12/2023, 10:00:00 AM