Nestjs操作mysql数据库_Nestjs typeOrm
NestJs仿小米商城项目实战视频教程(视频+课件+源码): https://www.itying.com/goods-1139.html
Nestjs数据库
Nest与数据库无关,允许您轻松地与任何SQL或NoSQL数据库集成。根据您的偏好,您有许多可用的选项。一般来说,将Nest连接到数据库只需为数据库加载一个适当的Node.js驱动程序,就像使用 Express 或Fastify一样。
您还可以直接使用任何通用的Node.js数据库集成库或ORM,例如 Sequelize (recipe)、knexjs (tutorial)`和 TypeORM ,以在更高的抽象级别上进行操作。
为了方便起见,Nest还提供了与现成的TypeORM与@nestjs/typeorm的紧密集成,我们将在本章中对此进行介绍,而与@nestjs/mongoose的紧密集成将在本章中介绍。这些集成提供了附加的特定于nestjs的特性,比如模型/存储库注入、可测试性和异步配置,从而使访问您选择的数据库更加容易。
Nestjs TypeORM 集成
为了与SQL和NoSQL数据库集成,Nest提供了@nestjs/typeorm包。Nest使用TypeORM是因为它是TypeScript中最成熟的对象关系映射器(ORM)。因为它是用TypeScript编写的,所以可以很好地与Nest框架集成。
为了开始使用它,我们首先安装所需的依赖项。在本章中,我们将演示如何使用流行的 Mysql ,TypeORM提供了对许多关系数据库的支持,比如PostgreSQL、Oracle、Microsoft SQL Server、SQLite,甚至像MongoDB这样的NoSQL数据库。我们在本章中介绍的过程对于TypeORM支持的任何数据库都是相同的。您只需为所选数据库安装相关的客户端API库。
$ npm install --save @nestjs/typeorm typeorm mysql
安装过程完成后,我们可以将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()相同的配置对象。另外,我们可以创建ormconfig.json,而不是将配置对象传递给forRoot()。
{
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "root",
"database": "test",
"entities": ["dist/**/*.entity{.ts,.js}"],
"synchronize": true
}
?> 静态全局路径(例如dist/**/*.entity{ .ts,.js})不适用于Webpack热重载。
然后,我们可以调用forRoot()没有任何选项:
app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [TypeOrmModule.forRoot()],
})
export class AppModule {}
一旦完成,TypeORM连接和EntityManager对象就可以在整个项目中注入(不需要导入任何模块),例如:
app.module.ts
import { Connection } from 'typeorm';
@Module({
imports: [TypeOrmModule.forRoot(), PhotoModule],
})
export class AppModule {
constructor(private readonly connection: Connection) {}
}
存储库模式
TypeORM支持存储库设计模式,因此每个实体都有自己的存储库。可以从数据库连接获得这些存储库。
为了继续这个示例,我们需要至少一个实体。我们将使用官方TypeORM文档中的Photo实体。
photo/photo.entity.ts
import { Entity, Column, 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。这是你决定在哪里保留你的模型文件。从我的观点来看,最好的方法是将它们放在他们的域中, 放在相应的模块目录中。
开始使用photo实体,我们需要让TypeORM知道它插入实体数组:
app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Photo } from './photo/photo.entity';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'test',
entities: [Photo],
synchronize: true,
}),
],
})
export class AppModule {}
现在让我们看一下PhotoModule:
photo.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PhotoService } from './photo.service';
import { PhotoController } from './photo.controller';
import { Photo } from './photo.entity';
@Module({
imports: [TypeOrmModule.forFeature([Photo])],
providers: [PhotoService],
controllers: [PhotoController],
})
export class PhotoModule {}
此模块使用forFeature()方法定义在当前范围中注册哪些存储库。这样,我们就可以使用@InjectRepository()装饰器将PhotoRepository注入到PhotoService中:
photo.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Photo } from './photo.entity';
@Injectable()
export class PhotoService {
constructor(
@InjectRepository(Photo)
private readonly photoRepository: Repository<Photo>,
) {}
findAll(): Promise<Photo[]> {
return this.photoRepository.find();
}
}
?> 不要忘记将PhotoModule导入根ApplicationModule。
如果要在导入TypeOrmModule.forFeature的模块之外使用存储库,则需要重新导出由其生成的提供程序。 您可以通过导出整个模块来做到这一点,如下所示:
photo.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Photo } from './photo.entity';
@Module({
imports: [TypeOrmModule.forFeature([Photo])],
exports: [TypeOrmModule]
})
export class PhotoModule {}
现在,如果我们在PhotoHttpModule中导入PhotoModule,我们可以在后一个模块的提供者中使用@InjectRepository(Photo)。
photo-http.module.ts
import { Module } from '@nestjs/common';
import { PhotoModule } from './photo.module';
import { PhotoService } from './photo.service';
import { PhotoController } from './photo.controller';
@Module({
imports: [PhotoModule],
providers: [PhotoService],
controllers: [PhotoController]
})
export class PhotoHttpModule {}
多个数据库
某些项目可能需要多个数据库连接。幸运的是,这也可以通过本模块实现。要使用多个连接,首先要做的是创建这些连接。在这种情况下,连接命名成为必填项。
假设你有一个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 {}
?> 如果未为连接设置任何name,则该连接的名称将设置为default。请注意,不应该有多个没有名称或同名的连接,否则它们会被覆盖。
此时,您的Photo、Person和Album实体中的每一个都已在各自的连接中注册。通过此设置,您必须告诉TypeOrmModule.forFeature()函数和@InjectRepository()装饰器应该使用哪种连接。如果不传递任何连接名称,则使用default连接。
@Module({
imports: [
TypeOrmModule.forFeature([Photo]),
TypeOrmModule.forFeature([Person], 'personsConnection'),
TypeOrmModule.forFeature([Album], 'albumsConnection'),
],
})
export class AppModule {}
您也可以为给定的连接注入Connection或EntityManager:
@Injectable()
export class PersonService {
constructor(
@InjectConnection('personsConnection')
private readonly connection: Connection,
@InjectEntityManager('personsConnection')
private readonly entityManager: EntityManager,
) {}
}
测试
在单元测试我们的应用程序时,我们通常希望避免任何数据库连接,从而使我们的测试适合于独立,并使它们的执行过程尽可能快。但是我们的类可能依赖于从连接实例中提取的存储库。那是什么?解决方案是创建假存储库。为了实现这一点,我们设置了自定义提供者。事实上,每个注册的存储库都由entitynamereposition标记表示,其中EntityName是实体类的名称。
@nestjs/typeorm包提供了基于给定实体返回准备好token的getRepositoryToken()函数。
@Module({
providers: [
PhotoService,
{
provide: getRepositoryToken(Photo),
useValue: mockRepository,
},
],
})
export class PhotoModule {}
现在, 将使用硬编码mockRepository作为PhotoRepository。每当任何提供程序使用@InjectRepository()装饰器请求PhotoRepository时,Nest会使用注册的mockRepository对象。
定制存储库
TypeORM提供称为自定义存储库的功能。要了解有关它的更多信息,请访问此页面。基本上,自定义存储库允许您扩展基本存储库类,并使用几种特殊方法对其进行丰富。
要创建自定义存储库,请使用@EntityRepository()装饰器和扩展Repository类。
@EntityRepository(Author)
export class AuthorRepository extends Repository<Author> {}
?>@EntityRepository()和Repository来自typeorm包。
创建类后,下一步是将实例化责任移交给Nest。为此,我们必须将AuthorRepository类传递给TypeOrm.forFeature()函数。
@Module({
imports: [TypeOrmModule.forFeature([AuthorRepository])],
controller: [AuthorController],
providers: [AuthorService],
})
export class AuthorModule {}
之后,只需使用以下构造注入存储库:
@Injectable()
export class AuthorService {
constructor(private readonly authorRepository: AuthorRepository) {}
}
异步配置
通常,您可能希望异步传递模块选项,而不是事先传递它们。在这种情况下,使用forRootAsync()函数,提供了几种处理异步数据的方法。
第一种可能的方法是使用工厂函数:
TypeOrmModule.forRootAsync({
useFactory: () => ({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'test',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
});
我们的工厂的行为与任何其他异步提供者一样(例如,它可以是异步的,并且它能够通过注入注入依赖)。
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],
});
或者,您可以使用类而不是工厂。
TypeOrmModule.forRootAsync({
useClass: TypeOrmConfigService,
});
上面的构造将TypeOrmConfigService在内部进行实例化TypeOrmModule,并将利用它来创建选项对象。在TypeOrmConfigService必须实现TypeOrmOptionsFactory的接口。
@Injectable()
class TypeOrmConfigService implements TypeOrmOptionsFactory {
createTypeOrmOptions(): TypeOrmModuleOptions {
return {
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'test',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
};
}
}
为了防止在TypeOrmModule中创建TypeOrmConfigService并使用从不同模块导入的提供程序,可以使用useExisting语法。
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useExisting: ConfigService,
});
这个构造与useClass的工作原理相同,但有一个关键的区别 —TypeOrmModule将查找导入的模块来重用现有的ConfigService,而不是实例化一个新的ConfigService。
示例
这儿有一个可用的例子。