TypeORM SQL

导读

本章仅适用于 TypeScript

警告 在本文中,您将学习如何使用自定义提供程序机制从头开始基于 TypeORM 包创建 DatabaseModule。因此,此解决方案包含大量开销,您可以使用现成的、开箱即用的专用 @nestjs/typeorm 包来省略这些开销。要了解更多信息,请参阅 此处

TypeORM 绝对是 node.js 世界中可用的最成熟的对象关系映射器 (ORM)。由于它是用 TypeScript 编写的,因此它与 Nest 框架配合得很好。

入门

要开始使用此库,我们必须安装所有必需的依赖项:

bash
$ npm install --save typeorm mysql2

我们需要做的第一步是使用从 typeorm 包导入的 new DataSource().initialize() 类与我们的数据库建立连接。initialize() 函数返回一个 Promise,因此我们必须创建一个 异步提供程序

database.providers
ts
import { DataSource } from 'typeorm'

export const databaseProviders = [
  {
    provide: 'DATA_SOURCE',
    useFactory: async () => {
      const dataSource = new DataSource({
        type: 'mysql',
        host: 'localhost',
        port: 3306,
        username: 'root',
        password: 'root',
        database: 'test',
        entities: [
          `${__dirname}/../**/*.entity{.ts,.js}`,
        ],
        synchronize: true,
      })

      return dataSource.initialize()
    },
  },
]
警告

在生产中不应使用设置 synchronize: true - 否则您可能会丢失生产数据。

提示

按照最佳实践,我们在带有 *.providers.ts 后缀的分离文件中声明了自定义提供程序。

然后,我们需要导出这些提供程序,使它们可供应用程序的其余部分访问

database.module
ts
import { Module } from '@nestjs/common'
import { databaseProviders } from './database.providers'

@Module({
  providers: [...databaseProviders],
  exports: [...databaseProviders],
})
export class DatabaseModule {}

现在我们可以使用 @Inject() 装饰器注入 DATA_SOURCE 对象。每个依赖于 DATA_SOURCE 异步提供程序的类都将等待,直到 Promise 被解析。

存储库模式

TypeORM 支持存储库设计模式,因此每个实体都有自己的存储库。这些存储库可以从数据库连接中获取。

但首先,我们至少需要一个实体。我们将重用官方文档中的 Photo 实体。

photo.entity
ts
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'

@Entity()
export class Photo {
  @PrimaryGeneratedColumn()
  id: number

  @Column({ length: 500 })
  name: string

  @Column('text')
  description: string

  @Column()
  filename: string

  @Column('int')
  views: number

  @Column()
  isPublished: boolean
}

Photo 实体属于 photo 目录。此目录代表 PhotoModule。现在,让我们创建一个 Repository 提供程序:

photo.providers
ts
import { DataSource } from 'typeorm'
import { Photo } from './photo.entity'

export const photoProviders = [
  {
    provide: 'PHOTO_REPOSITORY',
    useFactory: (dataSource: DataSource) => dataSource.getRepository(Photo),
    inject: ['DATA_SOURCE'],
  },
]
警告

在实际应用中,您应该避免使用魔法字符串PHOTO_REPOSITORYDATA_SOURCE 都应保存在单独的 constants.ts 文件中。

现在我们可以使用 @Inject() 装饰器将 Repository<Photo> 注入到 PhotoService

photo.service
ts
import { Inject, Injectable } from '@nestjs/common'
import { Repository } from 'typeorm'
import { Photo } from './photo.entity'

@Injectable()
export class PhotoService {
  constructor(
    @Inject('PHOTO_REPOSITORY')
    private photoRepository: Repository<Photo>,
  ) {}

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

数据库连接是异步的,但 Nest 使这个过程对最终用户完全不可见。PhotoRepository 正在等待数据库连接,而 PhotoService 则被延迟,直到存储库准备好使用。当每个类被实例化时,整个应用程序就可以启动。

这是最终的 PhotoModule

photo.module
ts
import { Module } from '@nestjs/common'
import { DatabaseModule } from '../database/database.module'
import { photoProviders } from './photo.providers'
import { PhotoService } from './photo.service'

@Module({
  imports: [DatabaseModule],
  providers: [
    ...photoProviders,
    PhotoService,
  ],
})
export class PhotoModule {}
提示

不要忘记将 PhotoModule 导入到根 AppModule