nest 数据库集成

时间:2023-01-20 浏览:193 分类:NestJS

Nest 与数据库无关,使您可以轻松地与任何 SQL 或 NoSQL 数据库集成。根据您的喜好,您有多种选择。在最一般的层面上,将 Nest 连接到数据库只是为数据库加载适当的 Node.js 驱动程序的问题,就像使用 Express 或 Fastify 一样。您还可以直接使用任何通用的 Node.js 数据库集成库或 ORM,例如 MikroORM(参见 MikroORM 配方)、Sequelize(参见 Sequelize 集成)、Knex.js(参见 Knex.js 教程)、TypeORM 和 Prisma(参见Prisma recipe),以在更高的抽象层次上运行。

为了方便起见,Nest 提供了与 TypeORM 的紧密集成,开箱即用的 Sequelize 分别使用 @nestjs/typeorm 和 @nestjs/sequelize 包,我们将在本章中介绍,Mongoose 与 @nestjs/mongoose,本章将对此进行介绍。这些集成提供了额外的 NestJS 特定功能,例如模型/存储库注入、可测试性和异步配置,使访问您选择的数据库更加容易。

为了与 SQL 和 NoSQL 数据库集成,Nest 提供了 @nestjs/typeorm 包。 Nest 使用 TypeORM,因为它是可用于 TypeScript 的最成熟的对象关系映射器 (ORM)。由于它是用 TypeScript 编写的,因此可以很好地与 Nest 框架集成。

要开始使用它,我们首先安装所需的依赖项。在本章中,我们将演示使用流行的 MySQL 关系型 DBMS,但 TypeORM 提供对许多关系型数据库的支持,例如 PostgreSQL、Oracle、Microsoft SQL Server、SQLite,甚至像 MongoDB 这样的 NoSQL 数据库。我们在本章中完成的过程对于 TypeORM 支持的任何数据库都是相同的。您只需为所选数据库安装关联的客户端 API 库。

第一步:安装依赖

$ npm install --save @nestjs/typeorm typeorm mysql2   // for mysql
$ npm install --save @nestjs/typeorm typeorm pg      // for postgre

第二步:配置链接信息

app.module.tsJS

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

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'test',
      entities: [],
      synchronize: true,
    }),
  ],
})
export class AppModule {}

也可以异步加载

TypeOrmModule.forRootAsync({
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (configService: ConfigService) => ({
    type: 'mysql',
    host: configService.get('HOST'),
    port: +configService.get('PORT'),
    username: configService.get('USERNAME'),
    password: configService.get('PASSWORD'),
    database: configService.get('DATABASE'),
    entities: [],
    synchronize: true,
  }),
});

具体可选配置参数在这里

完成后,TypeORM DataSource 和 EntityManager 对象将可用于在整个项目中注入(无需导入任何模块)。

第三步:建立存储库数据模型

user.entity.ts

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

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

  @Column()
  firstName: string;

  @Column()
  lastName: string;

  @Column({ default: true })
  isActive: boolean;
}

用户实体文件位于用户目录中。该目录包含与 UsersModule 相关的所有文件。可以决定将模型文件保存在何处,还是建议在相应的模块目录中的域附近创建它们。

要开始使用 User 实体,我们需要通过将其插入模块 forRoot() 方法选项中的实体数组来让 TypeORM 知道它(除非您使用静态 glob 路径):

    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'test',
      entities: [User],  // 手动指定实体,或者使用自动加载autoLoadEntities
      synchronize: true,
    }),

第四步:相应模块注册存储库

users.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { User } from './user.entity';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  providers: [UsersService],
  controllers: [UsersController],
})
export class UsersModule {}

该模块使用 forFeature() 方法来定义在当前范围内注册了哪些存储库。有了它,就可以使用 @InjectRepository() 装饰器将 UsersRepository 注入到 UsersService 中。

第五步:注入服务操作数据库

users.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
  ) {}

  findAll(): Promise<User[]> {
    return this.usersRepository.find();
  }

  findOne(id: number): Promise<User> {
    return this.usersRepository.findOneBy({ id });
  }

  async remove(id: string): Promise<void> {
    await this.usersRepository.delete(id);
  }
}

数据操作api


save(user)      创建:返回该数据的所有字段  { name: 'test', age: 18, h: 9, isActive: true }
insert(user)    创建:返回数据插入结果对象   ** 推荐

// insert返回结果
InsertResult {
  identifiers: [ { h: 8 } ],
  generatedMaps: [ { h: 8, isActive: true } ],
  raw: ResultSetHeader {
    fieldCount: 0,
    affectedRows: 1,
    insertId: 8,
    info: '',
    serverStatus: 2,
    warningStatus: 0
  }
}

delete(user)   删除数据表中相应字段   ** 推荐
remove(user)   删除数据表实体

// 用法:delete({name: 'test'});
// let data = await this.userRepository.findOne({where: {name: 'test'}});
// await this.userRepository.remove(data);

find({where: {name: 'test'}});   查找所有符合定位数据,返回数据列表,找不到返回空列表[]
findOne({where: {name: 'test'}});    查找第一个符合定位数据,返回当前数据,找不到返回空 null
findAndCount({where: {name: 'test'}});    查找所有符合定位数据,返回数据列表,和总数统计
findAndCountBy({name: 'test'})   查找所有符合定位数据,返回数据列表,和总数统计

update({name: 'test'}, {age: 20});    修改对应字段数据,返回修改结果对象

// 返回 UpdateResult { generatedMaps: [], raw: [], affected: 7 }

其他操作方法

increment({name: 'test'}, 'age', '4');  增加数值,成功返回改变实体
decrement({name: 'test'}, 'age', '4');  减少数值,成功返回改变实体
count({where: {name: 'test'}});  计数
countBy({name: 'test'});  计数

运算符

Not  !=
LessThan  <
LessThanOrEqual   <=
MoreThan    >
MoreThanOrEqual   >=
Equal  ==
Between   Between(1,10)
IsNull   IsNull()