Node.js又一框架:Nest.js(七)

1,145 阅读3分钟

数据库

Nest 与数据库无关,可以轻松地与任何 SQLNoSQL 数据库集成:

  1. 为数据库加载一个适当的 Node.js 驱动程序。
  2. 直接使用任何通用的 Node.js 数据库集成库或 ORM ,如:Sequelize (recipe)knexjstutorial)和 TypeORM ,以在更高的抽象级别上进行操作。
  3. 与现成的 TypeORM@nestjs/typeorm)的紧密集成:这些集成提供了附加的特定于 nestjs 的特性,比如模型/存储库注入、可测试性和异步配置,从而使访问您选择的数据库更加容易。

typeORM 集成

TypeORM 是 TypeScript 中最成熟的对象关系映射器(ORM),提供了对许多关系数据库的支持,如:PostgreSQL 、OracleMicrosoft SQL ServerSQLite,甚至像 MongoDB这样的 NoSQL 数据库。

Nest 提供了 @nestjs/typeorm 包。只需为所选数据库安装相关的客户端 API 库即可。

  1. 安装所需的依赖项。

    npm install --save @nestjs/typeorm typeorm mysql2
    
  2. 将 TypeOrmModule 导入AppModule

    // app.module.ts
    
    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 {}
    

    forRoot() 方法支持所有 TypeORM 包中 createConnection() 函数暴露出的配置属性,详见

    参数说明
    retryAttempts重试连接数据库的次数(默认:10)
    retryDelay两次重试连接的间隔(ms)(默认:3000)
    autoLoadEntities自动加载实体标识(默认:false,自动加载)
    keepConnectionAlive在应用程序关闭后连接是否保持(默认:false,关闭)

    或:创建 ormconfig.json 文件,然后不带任何选项地调用 forRoot()

    // ormconfig.json
    {
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'test',
      entities: [],
      synchronize: true,
    }
    
    // app.module.ts
    
    import { Module } from '@nestjs/common';
    import { TypeOrmModule } from '@nestjs/typeorm';
    
    @Module({
      imports: [
        TypeOrmModule.forRoot(),
      ],
    })
    export class AppModule {}
    

    但是,ormconfig.json 文件由 typeorm 库载入,任何上述参数之外的属性都不会被应用。如:由 forRoot() 方法内部支持的属性 autoLoadEntitiesretryDelay()

存储库模式

TypeORM 支持存储库设计模式,因此每个实体都有自己的存储库。可以从数据库连接获得这些存储库(依赖包:typeorm-model-generator)。

实体

  1. 定义一个实体。

    /**
     * @description: 真实PK用户清单实体文件
     * @update: 2021-09-11 18:33:41
     * @author: Ada.H
     */
    
    // pk-user.entity.ts
     
    import { Entity, Column } from 'typeorm';
    
    @Entity('user_is_kp_info', { schema: 'operator_data' })
    export class PkUser {
      @Column('varchar', {
        primary: true,
        name: 'phone',
        comment: '电话',
        length: 16,
      })
      phone: string
      
      @Column('varchar', {
        name: 'user_name',
        nullable: true,
        comment: '用户名称',
        length: 64,
      })
      userName: string | null
    
      @Column('varchar', {
        name: 'post_name',
        nullable: true,
        comment: '角色',
        length: 512,
      })
      postName: string | null
    
      @Column('varchar', {
        name: 'is_kp',
        comment: '是否kp用户',
        length: 2,
        default: () => "'1'",
      })
      isKp: string
      
      @Column('datetime', {
        name: 'update_time',
        nullable: true,
        comment: '更新时间',
        default: () => 'CURRENT_TIMESTAMP',
      })
      updateTime: Date | null
    
      @Column('datetime', {
        name: 'create_time',
        nullable: true,
        comment: '创建时间',
      })
      createTime: Date | null
    }
    

    pk-user 实体在 pk-users 目录下,该目录包含了和 pk-users.module 模块有关的所有文件。可以自定义在哪里保存模型文件,但推荐在相应的模块目录中保存模型文件。

  2. 在模块的 forRoot() 方法的选项中将它插入 entities 数组中来让 TypeORM 知道它的存在。

    // app.module.ts
    
    import { PkUser } from './pk-users/pk-user.entity'
    
    // 非静态的全局路劲,手动单独插入
    entities: [PkUser],
    
    // 静态的全局路径,无需单独插入
    entities: ['dist/**/*.entity{.ts,.js}'],  
    
  3. 业务模块中使用 forFeature() 方法定义在当前范围中注册哪些存储库。

    // pk-users.module.ts
    
    import { Module } from '@nestjs/common'
    import { TypeOrmModule } from '@nestjs/typeorm'
    import { PkUsersService } from './pk-users.service'
    import { PkUsersController } from './pk-users.controller'
    import { PkUser } from './entities/pk-user.entity'
    
    @Module({
      imports: [TypeOrmModule.forFeature([PkUser])],
      controllers: [PkUsersController],
      providers: [PkUsersService],
    })
    export class PkUsersModule {}
    
  4. 使用 @InjectRepository() 装饰器将 PkUsersRepository 注入到 PkUsersService 中。

    // pk-users.service.ts
    
    import { Injectable } from '@nestjs/common';
    import { InjectRepository } from '@nestjs/typeorm';
    import { Repository } from 'typeorm';
    import { PkUser } from './pk-user.entity';
    
    @Injectable()
    export class PkUsersService {
      constructor(
        @InjectRepository(PkUser)
        private pkUsersRepository: Repository<PkUser>
      ) {}
    
      findAll(): Promise<PkUser[]> {
        return this.pkUsersRepository.find();
      }
    
      findOne(id: string): Promise<PkUser> {
        return this.pkUsersRepository.findOne(id);
      }
    
      async remove(id: string): Promise<void> {
        await this.pkUsersRepository.delete(id);
      }
    }