乐闻世界logo
搜索文章和话题

NestJS 如何集成数据库?

2月17日 22:40

数据库集成概述

NestJS 提供了对多种数据库的完整支持,包括关系型数据库(如 MySQL、PostgreSQL)和非关系型数据库(如 MongoDB)。通过使用 TypeORM、Prisma、Mongoose 等 ORM/ODM 库,开发者可以轻松实现数据库操作。

TypeORM 集成

安装依赖

bash
npm install @nestjs/typeorm typeorm npm install mysql2 # 或 pg、sqlite3、better-sqlite3 等

配置 TypeORM

typescript
import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './entities/user.entity'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'test', entities: [User], synchronize: true, // 生产环境应设为 false logging: true, }), TypeOrmModule.forFeature([User]), ], providers: [UserService], controllers: [UserController], }) export class UserModule {}

创建实体

typescript
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; @Entity('users') export class User { @PrimaryGeneratedColumn() id: number; @Column({ unique: true }) email: string; @Column() password: string; @Column() name: string; @Column({ default: true }) isActive: boolean; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; }

创建 Repository

typescript
import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { User } from './entities/user.entity'; import { CreateUserDto } from './dto/create-user.dto'; @Injectable() export class UserService { constructor( @InjectRepository(User) private userRepository: Repository<User>, ) {} async create(createUserDto: CreateUserDto): Promise<User> { const user = this.userRepository.create(createUserDto); return this.userRepository.save(user); } async findAll(): Promise<User[]> { return this.userRepository.find(); } async findOne(id: number): Promise<User> { return this.userRepository.findOne({ where: { id } }); } async update(id: number, updateUserDto: any): Promise<User> { await this.userRepository.update(id, updateUserDto); return this.findOne(id); } async remove(id: number): Promise<void> { await this.userRepository.delete(id); } async findByEmail(email: string): Promise<User> { return this.userRepository.findOne({ where: { email } }); } }

Prisma 集成

安装依赖

bash
npm install @prisma/client npm install prisma --save-dev npx prisma init

配置 Prisma

prisma
// prisma/schema.prisma datasource db { provider = "mysql" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" } model User { id Int @id @default(autoincrement()) email String @unique password String name String isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }

创建 Prisma 服务

typescript
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; import { PrismaClient } from '@prisma/client'; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { async onModuleInit() { await this.$connect(); } async onModuleDestroy() { await this.$disconnect(); } }

使用 Prisma

typescript
import { Injectable } from '@nestjs/common'; import { PrismaService } from './prisma.service'; import { CreateUserDto } from './dto/create-user.dto'; @Injectable() export class UserService { constructor(private prisma: PrismaService) {} async create(createUserDto: CreateUserDto) { return this.prisma.user.create({ data: createUserDto, }); } async findAll() { return this.prisma.user.findMany(); } async findOne(id: number) { return this.prisma.user.findUnique({ where: { id }, }); } async update(id: number, updateUserDto: any) { return this.prisma.user.update({ where: { id }, data: updateUserDto, }); } async remove(id: number) { return this.prisma.user.delete({ where: { id }, }); } }

Mongoose 集成

安装依赖

bash
npm install @nestjs/mongoose mongoose

配置 Mongoose

typescript
import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; import { User, UserSchema } from './schemas/user.schema'; import { UserService } from './user.service'; import { UserController } from './user.controller'; @Module({ imports: [ MongooseModule.forRoot('mongodb://localhost:27017/nest'), MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), ], providers: [UserService], controllers: [UserController], }) export class UserModule {}

创建 Schema

typescript
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document } from 'mongoose'; @Schema() export class User extends Document { @Prop({ required: true, unique: true }) email: string; @Prop({ required: true }) password: string; @Prop({ required: true }) name: string; @Prop({ default: true }) isActive: boolean; @Prop({ default: Date.now }) createdAt: Date; @Prop({ default: Date.now }) updatedAt: Date; } export const UserSchema = SchemaFactory.createForClass(User);

创建 Mongoose 服务

typescript
import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { User } from './schemas/user.schema'; import { CreateUserDto } from './dto/create-user.dto'; @Injectable() export class UserService { constructor( @InjectModel(User.name) private userModel: Model<User>, ) {} async create(createUserDto: CreateUserDto): Promise<User> { const createdUser = new this.userModel(createUserDto); return createdUser.save(); } async findAll(): Promise<User[]> { return this.userModel.find().exec(); } async findOne(id: string): Promise<User> { return this.userModel.findOne({ _id: id }).exec(); } async update(id: string, updateUserDto: any): Promise<User> { return this.userModel.findByIdAndUpdate(id, updateUserDto, { new: true }).exec(); } async remove(id: string): Promise<User> { return this.userModel.findByIdAndDelete(id).exec(); } }

数据库迁移

TypeORM 迁移

bash
# 生成迁移 npm run typeorm migration:generate -- -n CreateUserMigration # 运行迁移 npm run typeorm migration:run # 回滚迁移 npm run typeorm migration:revert

Prisma 迁移

bash
# 创建迁移 npx prisma migrate dev --name init # 应用迁移 npx prisma migrate deploy # 重置数据库 npx prisma migrate reset

事务处理

TypeORM 事务

typescript
import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, DataSource } from 'typeorm'; import { User } from './entities/user.entity'; import { Order } from './entities/order.entity'; @Injectable() export class OrderService { constructor( private dataSource: DataSource, @InjectRepository(User) private userRepository: Repository<User>, @InjectRepository(Order) private orderRepository: Repository<Order>, ) {} async createOrderWithUser(userId: number, orderData: any) { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { const user = await queryRunner.manager.findOne(User, { where: { id: userId } }); const order = queryRunner.manager.create(Order, { ...orderData, user, }); await queryRunner.manager.save(order); await queryRunner.commitTransaction(); return order; } catch (err) { await queryRunner.rollbackTransaction(); throw err; } finally { await queryRunner.release(); } } }

Prisma 事务

typescript
async createOrderWithUser(userId: number, orderData: any) { return this.prisma.$transaction(async (prisma) => { const user = await prisma.user.findUnique({ where: { id: userId }, }); const order = await prisma.order.create({ data: { ...orderData, user: { connect: { id: userId }, }, }, }); return order; }); }

关系映射

TypeORM 关系

typescript
@Entity('users') export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @OneToMany(() => Order, order => order.user) orders: Order[]; } @Entity('orders') export class Order { @PrimaryGeneratedColumn() id: number; @Column() userId: number; @ManyToOne(() => User, user => user.orders) user: User; @Column() product: string; }

Prisma 关系

prisma
model User { id Int @id @default(autoincrement()) name String orders Order[] } model Order { id Int @id @default(autoincrement()) userId Int user User @relation(fields: [userId], references: [id]) product String }

数据库连接池配置

TypeORM 连接池

typescript
TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'test', entities: [__dirname + '/**/*.entity{.ts,.js}'], synchronize: false, extra: { connectionLimit: 10, }, })

Prisma 连接池

prisma
datasource db { provider = "mysql" url = env("DATABASE_URL") // 连接池配置 connection_limit = 10 }

最佳实践

  1. 使用 DTO:使用数据传输对象来验证和转换数据
  2. 环境变量:使用环境变量管理数据库配置
  3. 迁移管理:使用迁移来管理数据库结构变更
  4. 事务处理:在需要原子性操作时使用事务
  5. 索引优化:为常用查询字段添加索引
  6. 连接池:合理配置连接池大小
  7. 软删除:实现软删除而不是物理删除
  8. 查询优化:避免 N+1 查询问题

总结

NestJS 数据库集成提供了:

  • 对多种数据库的支持
  • 灵活的 ORM/ODM 选择
  • 完整的类型安全
  • 强大的关系映射
  • 便捷的事务处理

掌握数据库集成是构建数据驱动的 NestJS 应用程序的基础。通过合理选择和使用 ORM/ODM,遵循最佳实践,可以构建出高性能、可维护的数据访问层。

标签:NestJS