nest系列 - 连接数据库
前言
使用 服务端框架
离不开要连接数据库, 主要的连接方式有 使用直接连接 特定的数据库,或者使用 orm链接,今天已 mongodb
举例,来总结一下数据库的连接方式
链接方式
🚀直接链接
直接使用数据库提供的方法,可能每种数据库的连接方式都不一样
方式1 - 直接使用 mongoose
初始化
在开始使用这个库前,我们必须安装所有必需的依赖关系
pnpm install --save mongoose
我们需要做的第一步是使用 connect()
函数建立与数据库的连接。connect()
函数返回一个 Promise
,因此我们必须创建一个 异步提供者。
database.providers.ts
import * as mongoose from 'mongoose';
export const databaseProviders = [
{
provide: 'DATABASE_CONNECTION',
useFactory: (): Promise<typeof mongoose> =>
mongoose.connect('mongodb://localhost/nest'),
},
];
然后,我们需要导出这些提供者,以便应用程序的其余部分可以 访问 它们。
database.module.ts
import { Module } from '@nestjs/common';
import { databaseProviders } from './database.providers';
@Module({
providers: [...databaseProviders],
exports: [...databaseProviders],
})
export class DatabaseModule {}
现在我们可以使用 @Inject()
装饰器注入 Connection
对象。依赖于 Connection
异步提供者的每个类都将等待 Promise
被解析。
模型注入
使用Mongoose,一切都来自Schema。 让我们定义 CatSchema
:
schemas/cats.schema.ts
import * as mongoose from 'mongoose';
export const CatSchema = new mongoose.Schema({
name: String,
age: Number,
breed: String,
date: { type: Date, default: Date.now }
});
CatsSchema
属于 cats
目录。此目录代表 CatsModule
。
cats.providers.ts
现在,让我们创建一个 模型 提供者:
import { Connection } from 'mongoose';
import { CatSchema } from './cat.schema';
export const catsProviders = [
{
provide: 'CAT_MODEL',
useFactory: (connection: Connection) => connection.model('Cat', CatSchema),
inject: ['DATABASE_CONNECTION'],
},
];
请注意,在实际应用程序中,您应该避免使用魔术字符串。
CAT_MODEL
和DATABASE_CONNECTION
都应保存在分离的constants.ts
文件中。
现在我们可以使用 @Inject()
装饰器将 CAT_MODEL
注入到 CatsService
中:
cats.service.ts
import { Model } from 'mongoose';
import { Injectable, Inject } from '@nestjs/common';
import { Cat } from './cat.interface';
import { CreateCatDto } from './create-cat.dto';
@Injectable()
export class CatsService {
constructor(
@Inject('CAT_MODEL') private catModel: Model<Cat>,
) {}
async create(createCatDto: CreateCatDto): Promise<Cat> {
const createdCat = new this.catModel(createCatDto);
return createdCat.save();
}
async findAll(): Promise<Cat[]> {
return this.catModel.find().exec();
}
}
在上面的例子中,我们使用了 Cat
接口。 此接口扩展了来自 mongoose
包的 Document
:
import { Document } from 'mongoose';
export interface Cat extends Document {
readonly name: string;
readonly age: number;
readonly breed: string;
}
这是一个最终的 CatsModule
:
import { Module } from '@nestjs/common';
import { CatsController } from "./cat.controller";
import { CatsService } from './cat.service';
import { catsProviders } from './cats.providers';
import { DatabaseModule } from '../database/database.module';
@Module({
imports: [DatabaseModule],
controllers: [CatsController],
providers: [
CatsService,
...catsProviders,
],
})
export class CatsModule {}
不要忘记将
CatsModule
导入到根ApplicationModule
中。
结果
运行create
方法,然后查看数据库
解释一下这个数据库中具体名字的来源
- 数据库名称
在文件
database.providers.ts
中,使用mongoose.connect('mongodb://localhost/nest')
进行链接,这个nest
是数据库名称 - 表名
在文件
cats.providers.ts
中,使用useFactory: (connection: Connection) => connection.model('Cat', CatSchema)
创建了这张表,由于在mongodb
表名中是复数形式,所以变成了Cats
方式2 - 使用工具 @nestjs/mongoose
上文中的 直接使用mongoose链接
是一种通用的连接方式,有点繁琐,我们介绍一个在nest
环境中链接mongodb
的方式
初始化
pnpm install --save mongoose
pnpm install --save @nestjs/mongoose
我们第一步需要建立数据库的链接,使用 @nestjs/mongoose
提供的 MongooseModule
的 forRoot
方法链接数据库
app.module.ts
import { MongooseModule } from '@nestjs/mongoose';
import { DogModule } from './dog/dog.module';
@Module({
imports: [DogModule,MongooseModule.forRoot('mongodb://localhost/test')],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
创建数据表
使用 @nestjs/mongoose
提供的 MongooseModule
的 forFeature
方法创建 Dog
表
dog.module.ts
import { Module } from "@nestjs/common";
import DogController from "./dog.controller";
import { DogService } from "./dog.service";
import { MongooseModule } from '@nestjs/mongoose';
import { DogSchema } from "./dog.schema";
@Module({
imports: [MongooseModule.forFeature([{ name: 'Dog', schema: DogSchema }]), ],
controllers: [DogController],
providers: [DogService],
})
export class DogModule {}
其中我们使用到了DogSchema
,由 SchemaFactory.createForClass
创建
DogSchema
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
// @Prop 装饰器接受一个可选的参数,通过这个,你可以指示这个属性是否是必须的,
//是否需要默认值,或者是标记它作为一个常量,
import { Document } from 'mongoose';
export type DogDocument = Dog & Document;
@Schema()
export class Dog extends Document {
// 设置默认值
@Prop({
default:"wangCai"
})
name: string;
// 设置值为必填
@Prop({ required: true })
// 设置类型
age: number;
@Prop({
type:Number
})
height: number;
}
// SchemaFactory 是 mongoose 内置的一个方法做用是读取模式文档 并创建 Schema 对象
export const DogSchema = SchemaFactory.createForClass(Dog);
注入
使用 InjectModel
装饰器注入表名
,表名即为 MongooseModule.forFeature
中的 name
属性, 同时使用 Model<DogDocument>
作为 dog
的类型
dog.service.ts
import { Injectable } from "@nestjs/common";
import { InjectModel } from "@nestjs/mongoose";
import { Model } from "mongoose";
import { CreateDogDto } from "./dog.dto";
import { DogDocument } from "./dog.schema";
@Injectable()
export class DogService {
constructor(@InjectModel("Dog") private dog: Model<DogDocument>) {}
async createDog(dog:CreateDogDto) {
const createDog = new this.dog(dog);
const temp = await createDog.save();
return temp;
}
}
上文使用了CreateDogDto
对入参格式简单描述一下,如果有对格式更精确的要求,可以使用 👉class-validator
CreateDogDto
export class CreateDogDto {
name: string;
age: number;
height: number;
sex: 0 | 1;
}
结果
使用createDog
并查看数据库
🚀 方式3 - typeorm 链接1
对象关系映射(Object Relational Mapping,简称ORM),优势是可以简化我们操作数据库的难度,简化操作,如想了解更多,👉阮一峰# ORM 实例教程
起步
pnpm i typeorm
pnpm i @nestjs/typeorm
链接数据库
使用 TypeOrmModule
的 forRoot
方法进行链接数据库,有兴趣可以点击👉docs查看更多关于 forRoot 的配置项,这里只说最简单的
app.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
DogModule,
MongooseModule.forRoot('mongodb://localhost/test'),
CatsModule,
FishModule,
TypeOrmModule.forRoot({
type:"mongodb",
url:"mongodb://localhost/fish",
entities:[Fish], // 加载实体
synchronize:true, // 实体字段是否同步
autoLoadEntities:true, // 自动加载实体
})
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
🔥🔥🔥 创建表
使用 TypeOrmModule
的forFeature
方法,接收一个 实体数组
,使用实体Fish
fish.module.ts
import { TypeOrmModule } from "@nestjs/typeorm";
import { FishController } from "./fish.controller";
import { FishService } from "./fish.service";
import { Fish } from "./fish.entity";
import { Module } from "@nestjs/common";
@Module({
imports: [TypeOrmModule.forFeature([Fish])],
controllers: [FishController],
providers: [FishService],
})
export class FishModule {}
上文提到了 实体Fish
,在实体中需要使用装饰器Entity
表明是一个实体类型,同时 class 类名表示的是表名,使用装饰器Column
表示列的名称,Column 可以传入多个参数 ,例如type
,required
,default
等,有兴趣可以戳这里 👉TypeOrm 中文文档
fish.entity.ts
import {
Entity,
Column,
PrimaryGeneratedColumn,
ObjectIdColumn,
ObjectID,
} from "typeorm";
@Entity()
// 表明
export class Fish {
//自增列
@ObjectIdColumn()
id: ObjectID;
//普通列
@Column()
name: string;
}
使用数据表
使用装饰器 InjectRepository
并传入实体 fish
,使用 Repository
注入
fish.service.ts
import { Fish } from "./fish.entity";
import { Injectable, Inject } from "@nestjs/common";
import { CreateFishDto } from "./create-fish.dto";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
@Injectable()
export class FishService {
constructor(
@InjectRepository(Fish)
private readonly userRepository: Repository<Fish>
) {}
async create(createCatDto: CreateFishDto) {
const a = new Fish();
a.name = createCatDto.name;
return this.userRepository.save(a);
}
}
结果
保存并调用create
方法
🚀🚀 方式3 - typeorm 链接2
创建数据库
创建文件夹database
,并在其内部创建 database.providers.ts
和 database.module.ts
文件,使其模块化
database.providers.ts
import { DataSource } from "typeorm";
export const databaseProviders = [
{
// Token可以自己设定
provide: "DbConnectionToken",
useFactory: async () => {
const ds = new DataSource({
type: "mongodb",
url: "mongodb://localhost/duck",
entities: [__dirname + "/../**/*.entity{.ts,.js}"],
});
ds.initialize();
return ds;
},
},
];
database.module.ts
import { Module } from '@nestjs/common';
import { databaseProviders } from './database.providers';
@Module({
providers: [...databaseProviders],
exports: [...databaseProviders],
})
export class DatabaseModule {}
创建数据表
duck.providers.ts
import { Provider } from "@nestjs/common";
import { DataSource } from "typeorm";
import { Duck } from "./duck.entity";
export const DuckProviders: Provider[] = [
{
provide: "DUCK_REPOSITORY",
useFactory: async (appDataSource:DataSource) => await appDataSource.getRepository(Duck),
// 注入 database.providers 的属性
inject: ["DbConnectionToken"],
},
];
其中 inject
中的 DbConnectionToken
是上文 databaseProviders
中的 porvide
字符串
同时自己也暴露出一个 provide 名称
- DUCK_REPOSITORY
, nest 会通过 provide
名称找到对应的 useFactory
简单定义一下 duck 实体
duck.entity.ts
import { Column, CreateDateColumn, Entity, ObjectID, ObjectIdColumn } from 'typeorm';
@Entity()
export class Duck {
@ObjectIdColumn()
_id:ObjectID
@Column()
name:String
@Column()
age:Number
@CreateDateColumn()
createAt:Date
}
链接数据库
引入 DatabaseModule
,同时把 DuckProviders
注入进来,由于上文 DuckProviders
被定义为数组,所以使用展开语法
duck.module.ts
import { Module } from "@nestjs/common";
import { DucksController } from "./duck.controller";
import { DucksService } from "./duck.service";
import { DuckProviders } from "./duck.providers";
import { DatabaseModule } from "../database/database.module";
@Module({
imports: [DatabaseModule],
controllers: [DucksController],
providers: [DucksService, ...DuckProviders],
})
export class DuckModule {}
使用
使用装饰器 Inject
并把上文中 DuckProviders
的 Provide
作为参数传入
同时使用MongoRepository
传入实体Duck
作为 duckRepository
的类型
duck.service
import { Injectable, Inject } from "@nestjs/common";
import { CreateDuckDto } from "./create-duck.dto";
import { MongoRepository } from "typeorm";
import { Duck } from "./duck.entity";
@Injectable()
export class DucksService {
constructor(
@Inject("DUCK_REPOSITORY")
private readonly duckRepository: MongoRepository<Duck>
) {}
async create(createDuckDto: CreateDuckDto) {
return this.duckRepository.save(createDuckDto);
}
}
简单定义一下dto
create-duck.dto.ts
export class CreateDuckDto {
name:string;
age : number;
}
执行结果
保存并执行 create
方法
总结
在学习 nest
的过程中,一定要 连接数据库,今天把 我遇到过的连接数据库的方法做个总结,没有好坏之分,只有合适与否,但是使用 orm
已经成为主流,使用同样的方式可以来连接多种数据库,最后一种是用的比较多的,今天是 周六,周末愉快🐟!
转载自:https://juejin.cn/post/7219552640938278973