TypeORM
在一个普通的后端业务开发中基本上逃离不了CRUD,也就是对数据的常规操作。日常对数据库的操作主要借助于SQL,至少需要掌握基础的SQL语法就有建表、增删改查等。但是如果想要在代码中直接实现对数据库的操作。就需要去编写大量的SQL,这在可读性、维护性、开发体验以及维护上都是非常糟糕的。
所以ORM框架也就应运而生。这一类框架是为了解决面向对象与关系型数据库存在的互不匹配的现象。把面向SQL开发转为面向对象开发。开发并不需要关注底层实现细节。可以以操作对象的模式使用数据库。
对象关系映射(ORM)模式是为了解决面向对象与关系型数据库存在的互不匹配现象的技术。
TypeORM 作为 Node.js 中老牌的 ORM 框架,无论是接口定义,还是代码实现方面都简单易懂、可读性高,也很容易对接多种数据源。
虽然市面上也有其他不错的 ORM 框架,比如 Sequelize、Prisma 等,但 TypeORM 使用 TypeScript 编写,在 NestJS 框架下运行得非常好,也是 NestJS 首推的 ORM 框架,有开箱即用的 @nestjs/typeorm 软件包支持。
综上所述,我们的 ORM 框架也将选用 TypeORM 来开发(看个人喜好与需求,如果喜欢 GraphQL 的,使用 Prisma 更好)。
封装
NestJS 使用 TypeORM 的方式有两种。一种是 NestJS 提供的 @nestjs/typeorm 集成包,可以导出 TypeOrmModule.forRoot 方法来连接数据库,同时可以使用 ormconfig.json 将数据库链接配置项剥离。另外一种是直接使用 typeorm,自由封装 Providers 导入使用。
两种方案各有优缺点,使用 @nestjs/typeorm 集成的方案较为简便,但自建的业务脚手架需要两种数据库保证在开发中体验一致性,此外之前已经自定义了全局环境变量的配置,没有必要再多一个 ormconfig.json 的配置来增加额外理解成本,所以接下来我们将使用第二种方案来连接数据库。
第一步:
为了使用TypeORM,必须先安装如下依赖:
cnpm install typeorm mysql2 mongoose
第二步:
在dev.yaml中添加数据库配置参数:
MONGODB_CONFIG:
name: "fast_gateway_test" # 自定义次数据库链接名称
type: mongodb # 数据库链接类型
url: "mongodb://localhost:27017" # 数据库链接地址
username: "xxxx" # 数据库链接用户名
password: "123456" # 数据库链接密码
database: "fast_gateway_test" # 数据库名
entities: "mongo" # 自定义加载类型
logging: false # 数据库打印日志
synchronize: true # 是否开启同步数据表功能
第三步:
新建:src/common/database/database.providers.ts
import { DataSource, DataSourceOptions } from 'typeorm';
import { getConfig } from 'src/utils/index'
const path = require('path');
// 设置数据库类型
const databaseType: DataSourceOptions['type'] = 'mongodb';
const { MONGODB_CONFIG } = getConfig()
const MONGODB_DATABASE_CONFIG = {
...MONGODB_CONFIG,
type: databaseType,
entities: [path.join(__dirname, `../../**/*.${MONGODB_CONFIG.entities}.entity{.ts,.js}`)],
}
const MONGODB_DATA_SOURCE = new DataSource(MONGODB_DATABASE_CONFIG)
// 数据库注入
export const DatabaseProviders = [
{
provide: 'MONGODB_DATA_SOURCE',
useFactory: async () => {
await MONGODB_DATA_SOURCE.initialize()
return MONGODB_DATA_SOURCE
}
}
];
第四步
新建database.module.ts
import { Module } from '@nestjs/common';
import { DatabaseProviders } from './database.providers';
@Module({
providers: [...DatabaseProviders],
exports: [...DatabaseProviders],
})
export class DatabaseModule { }
现在我们已经封装好了Mongodb的Provider,如果需要引入Mysql或者其他类型数据库的话,只需要替换相应的参数,重复上述步骤就好了。
NestJS使用Mongodb
第一步
注册实体,创建src/user/user.mongo.entity.ts
import { Entity, Column, UpdateDateColumn, ObjectIdColumn } from 'typeorm';
@Entity()
export class User {
@ObjectIdColumn()
id?: number;
@Column({ default: null })
name: string;
}
如果说我们需要在我们的系统中注册一个实体类的化,那么我们必须将他的后缀名变成
entities
MongoDB 是无模式的,所以即使在配置参数开启了
synchronize,启动项目的时候也不会去数据库创建对应的表,所以不用奇怪,并没有出错,但Mysql在每次应用程序启动时自动同步表结构,容易造成数据丢失,生产环境记得关闭,以免造成无可预计的损失。
第二步
创建user.providers.ts;
import { User } from './user.mongo.entity';
export const UserProviders = [
{
provide: 'USER_REPOSITORY',
useFactory: async (AppDataSource) => await AppDataSource.getRepository(User),
inject: ['MONGODB_DATA_SOURCE'],
},
];
第三步
创建user.service.ts,新增用户service
import { Injectable,Inject } from '@nestjs/common';
import { MongoRepository } from 'typeorm';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import {User} from './entities/user.mongo.entity'
@Injectable()
export class UserService {
constructor(
@Inject('USER_REPOSITORY')
private userRepository: MongoRepository<User>
){
}
createOrSave(user){
return this.userRepository.save(user);
}
create(createUserDto: CreateUserDto) {
return 'This action adds a new user';
}
findAll() {
return `This action returns all user`;
}
findOne(id: number) {
return `This action returns a #${id} user`;
}
update(id: number, updateUserDto: UpdateUserDto) {
return `This action updates a #${id} user`;
}
remove(id: number) {
return `This action removes a #${id} user`;
}
}
第四步
创建user.controller.ts,添加新增用户的http方法
import { Controller, Post, Body, Query, Get } from '@nestjs/common';
import { UserService } from './user.service';
import { AddUserDto } from './user.dto';
@ApiTags('用户')
@Controller('user')
export class UserController {
constructor(
private readonly userService: UserService,
) { }
@ApiOperation({
summary: '新增用户',
})
@Post('/add')
create(@Body() user: AddUserDto) {
return this.userService.createOrSave(user);
}
}
user.dto.ts的内容如下:
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty } from 'class-validator';
export class AddUserDto {
@ApiProperty({ example: 123, })
id?: string;
@ApiProperty({ example: 'cookie' })
@IsNotEmpty()
name: string;
@ApiProperty({ example: 'cookieboty@qq.com' })
@IsNotEmpty()
email: string;
@ApiProperty({ example: 'cookieboty' })
@IsNotEmpty()
username: string;
}
第五步
创建 user.module.ts,将 controller、providers、service 等都引入后,切记将 user.module.ts 导入 app.module.ts 后才会生效,这一步别忘记了
import { Module } from '@nestjs/common';
import { DatabaseModule } from '@/common/database/database.module';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { UserProviders } from './user.providers';
@Module({
imports: [
DatabaseModule
],
controllers: [
UserController
],
providers: [...UserProviders, UserService],
exports: [UserService],
})
export class UserModule { }
以上部分都配置好了之后,我们就可以在swagger文档上面看到我们定义的接口了