TypeORM

Nest.js中的数据库操作

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

要开始使用它,我们首先安装所需的依赖项。在本章中,我们将演示如何使用流行的MySQL Relational DBMS,但是TypeORM提供了对许多关系数据库的支持,例如PostgreSQL,Oracle,Microsoft SQL Server,SQLite,甚至是NoSQL数据库,例如MongoDB。对于TypeORM支持的任何数据库。

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()相同的配置对象。另外,我们可以在项目根目录中创建ormconfig.json文件,而不是将配置对象传递给forRoot()

{
  "type": "mysql",
  "host": "localhost",
  "port": 3306,
  "username": "root",
  "password": "root",
  "database": "test",
  "entities": ["dist/**/*.entity{.ts,.js}"],
  "synchronize": true
}

然后在forRoot中不传入任何参数:

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

@Module({
  imports: [TypeOrmModule.forRoot()]
})
export class AppModule {}

TyperOrm也支持异步配置:

TypeOrmModule.forRootAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    type: "mysql",
    host: configService.getString("HOST"),
    port: configService.getString("PORT"),
    username: configService.getString("USERNAME"),
    password: configService.getString("PASSWORD"),
    database: configService.getString("DATABASE"),
    entities: [__dirname + "/**/*.entity{.ts,.js}"],
    synchronize: true
  }),
  inject: [ConfigService]
});

数据查询

完成此操作后,TypeORM ConnectionEntityManager对象将可用于在整个项目中注入(而无需导入任何模块,例如:

import { Connection } from "typeorm";

@Module({
  imports: [TypeOrmModule.forRoot(), PhotoModule]
})
export class AppModule {
  constructor(private readonly connection: Connection) {}
}

Repository

TypeORM支持存储库设计模式,因此每个实体都有自己的存储库。这些存储库可以从数据库连接中获取,首先在Module中需要声明依赖:

@Module({
  imports: [TypeOrmModule.forFeature([Photo])],
  providers: [PhotoService],
  controllers: [PhotoController]
})
export class PhotoModule {}

然后在Service中引入Repository

@Injectable()
export class PhotoService {
  constructor(
    @InjectRepository(Photo)
    private readonly photoRepository: Repository<Photo>
  ) {}

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

如果要在导入TypeOrmModule.forFeature的模块之外使用存储库,则需要重新导出由其生成的提供程序。您可以通过导出整个模块来做到这一点,如下所示:

@Module({
  imports: [TypeOrmModule.forFeature([Photo])],
  exports: [TypeOrmModule]
})
export class PhotoModule {}

多数据库

一些项目需要多个数据库连接。这也可以通过该模块来实现。要使用多个连接,请首先创建连接。在这种情况下,连接命名成为强制性的。假设您有一个Person实体和一个Album实体,它们分别存储在各自的数据库中。

const defaultOptions = {
  type: "postgres",
  port: 5432,
  username: "user",
  password: "password",
  database: "db",
  synchronize: true
};

@Module({
  imports: [
    TypeOrmModule.forRoot({
      ...defaultOptions,
      host: "photo_db_host",
      entities: [Photo]
    }),
    TypeOrmModule.forRoot({
      ...defaultOptions,
      name: "personsConnection",
      host: "person_db_host",
      entities: [Person]
    }),
    TypeOrmModule.forRoot({
      ...defaultOptions,
      name: "albumsConnection",
      host: "album_db_host",
      entities: [Album]
    })
  ]
})
export class AppModule {}

此时,您已将每个PhotoPersonAlbum实体注册为各自的连接。使用此设置,您必须告诉TypeOrmModule.forFeature()函数和@InjectRepository()装饰器应使用哪个连接。如果未传递任何连接名称,则使用默认连接。

@Module({
  imports: [
    TypeOrmModule.forFeature([Photo]),
    TypeOrmModule.forFeature([Person], "personsConnection"),
    TypeOrmModule.forFeature([Album], "albumsConnection")
  ]
})
export class AppModule {}

然后可以注入ConnectionEntityManager

@Injectable()
export class PersonService {
  constructor(
    @InjectConnection("personsConnection")
    private readonly connection: Connection,
    @InjectEntityManager("personsConnection")
    private readonly entityManager: EntityManager
  ) {}
}

Testing

在对应用程序进行单元测试时,我们通常希望避免建立数据库连接,使我们的测试套件保持独立,并尽可能快地执行它们。但是我们的类可能取决于从连接实例中拉出的存储库。我们该如何处理?解决方案是创建模拟存储库。为此,我们设置了自定义提供程序。每个注册的存储库都由 <EntityName>Repository 标记自动表示,其中EntityName是您的实体类的名称。

@Module({
  providers: [
    PhotoService,
    {
      provide: getRepositoryToken(Photo),
      useValue: mockRepository
    }
  ]
})
export class PhotoModule {}

现在,替代的嘲讽存储库将用作PhotoRepository。每当任何类使用@InjectRepository()装饰器要求提供PhotoRepository时,Nest都会使用已注册的mockRepository对象。

自定义Repository

TypeORM提供了一种称为自定义存储库的功能。自定义存储库允许您扩展基本存储库类,并使用几种特殊方法来丰富它。

@EntityRepository(Author)
export class AuthorRepository extends Repository<Author> {}

创建类后,下一步就是将实例化责任委托给Nest。为此,我们必须将AuthororRepository类传递给TypeOrm.forFeature()方法。

@Module({
  imports: [TypeOrmModule.forFeature([AuthorRepository])],
  controller: [AuthorController],
  providers: [AuthorService]
})
export class AuthorModule {}

然后用如下方式注入:

@Injectable()
export class AuthorService {
  constructor(private readonly authorRepository: AuthorRepository) {}
}
上一页