后端
2025年7月19日
16 分钟阅读
Nest.js 数据库集成实战:TypeORM 与 Prisma
Nest.js 数据库集成实战:TypeORM 与 Prisma
数据库是现代 Web 应用的核心。Nest.js 提供了多种数据库集成方案,最流行的是 TypeORM 和 Prisma。本文将介绍如何在 Nest.js 中集成这两种 ORM,实现数据的持久化操作。
TypeORM 集成
安装依赖
npm install @nestjs/typeorm typeorm mysql2
# 或使用 PostgreSQL
npm install @nestjs/typeorm typeorm pg
# 或使用 SQLite
npm install @nestjs/typeorm typeorm sqlite3配置 TypeORM
// 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: "password",
database: "testdb",
entities: [__dirname + "/**/*.entity{.ts,.js}"], // 实体文件路径
synchronize: true, // 开发环境自动同步,生产环境应设为 false
}),
],
})
export class AppModule {}定义实体(Entity)
// users/entities/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";
@Entity("users") // 表名
export class User {
@PrimaryGeneratedColumn() // 主键,自增
id: number;
@Column({ type: "varchar", length: 100 })
name: string;
@Column({ type: "varchar", length: 255, unique: true })
email: string;
@Column({ type: "varchar", length: 255 })
password: string;
@Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP" })
createdAt: Date;
@Column({ type: "timestamp", nullable: true })
updatedAt: Date;
}创建 Repository
// users/users.module.ts
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { User } from "./entities/user.entity";
import { UsersController } from "./users.controller";
import { UsersService } from "./users.service";
@Module({
imports: [TypeOrmModule.forFeature([User])], // 注册实体
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}在服务中使用 Repository
// users/users.service.ts
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";
import { UpdateUserDto } from "./dto/update-user.dto";
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>
) {}
async create(createUserDto: CreateUserDto): Promise<User> {
const user = this.usersRepository.create(createUserDto);
return await this.usersRepository.save(user);
}
async findAll(): Promise<User[]> {
return await this.usersRepository.find();
}
async findOne(id: number): Promise<User> {
return await this.usersRepository.findOne({ where: { id } });
}
async findByEmail(email: string): Promise<User> {
return await this.usersRepository.findOne({ where: { email } });
}
async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
await this.usersRepository.update(id, updateUserDto);
return this.findOne(id);
}
async remove(id: number): Promise<void> {
await this.usersRepository.delete(id);
}
}实体关系
一对一关系
// users/entities/user.entity.ts
import {
Entity,
Column,
PrimaryGeneratedColumn,
OneToOne,
JoinColumn,
} from "typeorm";
import { Profile } from "../../profiles/entities/profile.entity";
@Entity("users")
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToOne(() => Profile, (profile) => profile.user)
@JoinColumn()
profile: Profile;
}
// profiles/entities/profile.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, OneToOne } from "typeorm";
import { User } from "../../users/entities/user.entity";
@Entity("profiles")
export class Profile {
@PrimaryGeneratedColumn()
id: number;
@Column()
bio: string;
@OneToOne(() => User, (user) => user.profile)
user: User;
}一对多关系
// users/entities/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from "typeorm";
import { Post } from "../../posts/entities/post.entity";
@Entity("users")
export class User {
@PrimaryGeneratedColumn()
id: number;
@OneToMany(() => Post, (post) => post.author)
posts: Post[];
}
// posts/entities/post.entity.ts
import {
Entity,
Column,
PrimaryGeneratedColumn,
ManyToOne,
JoinColumn,
} from "typeorm";
import { User } from "../../users/entities/user.entity";
@Entity("posts")
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@ManyToOne(() => User, (user) => user.posts)
@JoinColumn({ name: "authorId" })
author: User;
}多对多关系
// posts/entities/post.entity.ts
import {
Entity,
Column,
PrimaryGeneratedColumn,
ManyToMany,
JoinTable,
} from "typeorm";
import { Category } from "../../categories/entities/category.entity";
@Entity("posts")
export class Post {
@PrimaryGeneratedColumn()
id: number;
@ManyToMany(() => Category, (category) => category.posts)
@JoinTable()
categories: Category[];
}
// categories/entities/category.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToMany } from "typeorm";
import { Post } from "../../posts/entities/post.entity";
@Entity("categories")
export class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToMany(() => Post, (post) => post.categories)
posts: Post[];
}查询构建器
// users/users.service.ts
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { User } from "./entities/user.entity";
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>
) {}
// 使用查询构建器
async findActiveUsers(): Promise<User[]> {
return await this.usersRepository
.createQueryBuilder("user")
.where("user.active = :active", { active: true })
.orderBy("user.createdAt", "DESC")
.getMany();
}
// 复杂查询
async findUsersWithPosts(): Promise<User[]> {
return await this.usersRepository
.createQueryBuilder("user")
.leftJoinAndSelect("user.posts", "post")
.where("post.published = :published", { published: true })
.getMany();
}
}Prisma 集成
安装 Prisma
npm install prisma @prisma/client
npx prisma init定义 Schema
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
password String
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}生成 Prisma Client
npx prisma generate
npx prisma migrate dev创建 Prisma Service
// prisma/prisma.service.ts
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 Module
// prisma/prisma.module.ts
import { Global, Module } from "@nestjs/common";
import { PrismaService } from "./prisma.service";
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}在服务中使用 Prisma
// users/users.service.ts
import { Injectable } from "@nestjs/common";
import { PrismaService } from "../prisma/prisma.service";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {}
async create(createUserDto: CreateUserDto) {
return await this.prisma.user.create({
data: createUserDto,
});
}
async findAll() {
return await this.prisma.user.findMany();
}
async findOne(id: number) {
return await this.prisma.user.findUnique({
where: { id },
});
}
async findByEmail(email: string) {
return await this.prisma.user.findUnique({
where: { email },
});
}
async update(id: number, updateUserDto: UpdateUserDto) {
return await this.prisma.user.update({
where: { id },
data: updateUserDto,
});
}
async remove(id: number) {
return await this.prisma.user.delete({
where: { id },
});
}
}Prisma 关系查询
// users/users.service.ts
async findUserWithPosts(id: number) {
return await this.prisma.user.findUnique({
where: { id },
include: {
posts: true, // 包含关联的 posts
},
});
}
async findUsersWithPublishedPosts() {
return await this.prisma.user.findMany({
where: {
posts: {
some: {
published: true,
},
},
},
include: {
posts: {
where: {
published: true,
},
},
},
});
}Prisma 事务
// users/users.service.ts
async createUserWithProfile(userData: CreateUserDto, profileData: CreateProfileDto) {
return await this.prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: userData,
});
const profile = await tx.profile.create({
data: {
...profileData,
userId: user.id,
},
});
return { user, profile };
});
}使用环境变量配置
创建配置文件
// config/database.config.ts
import { registerAs } from "@nestjs/config";
export default registerAs("database", () => ({
type: process.env.DB_TYPE || "mysql",
host: process.env.DB_HOST || "localhost",
port: parseInt(process.env.DB_PORT, 10) || 3306,
username: process.env.DB_USERNAME || "root",
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME || "testdb",
synchronize: process.env.NODE_ENV === "development",
}));使用配置
// app.module.ts
import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { TypeOrmModule } from "@nestjs/typeorm";
import databaseConfig from "./config/database.config";
@Module({
imports: [
ConfigModule.forRoot({
load: [databaseConfig],
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
...configService.get("database"),
entities: [__dirname + "/**/*.entity{.ts,.js}"],
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}实际应用:用户管理系统
完整的用户实体和服务
// users/entities/user.entity.ts (TypeORM)
import {
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
} from "typeorm";
@Entity("users")
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 100 })
name: string;
@Column({ length: 255, unique: true })
email: string;
@Column({ length: 255 })
password: string;
@Column({ default: true })
active: boolean;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}// users/users.service.ts
import { Injectable, NotFoundException } 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";
import { UpdateUserDto } from "./dto/update-user.dto";
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>
) {}
async create(createUserDto: CreateUserDto): Promise<User> {
const user = this.usersRepository.create(createUserDto);
return await this.usersRepository.save(user);
}
async findAll(): Promise<User[]> {
return await this.usersRepository.find({
where: { active: true },
order: { createdAt: "DESC" },
});
}
async findOne(id: number): Promise<User> {
const user = await this.usersRepository.findOne({ where: { id } });
if (!user) {
throw new NotFoundException(`User with ID ${id} not found`);
}
return user;
}
async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
const user = await this.findOne(id);
Object.assign(user, updateUserDto);
return await this.usersRepository.save(user);
}
async remove(id: number): Promise<void> {
const user = await this.findOne(id);
await this.usersRepository.remove(user);
}
}最佳实践
- 环境配置:使用环境变量管理数据库配置
- 实体验证:使用 class-validator 验证实体数据
- 迁移管理:使用数据库迁移而不是 synchronize(生产环境)
- 连接池:合理配置数据库连接池
- 错误处理:捕获和正确处理数据库错误
- 查询优化:避免 N+1 查询问题,合理使用 relations
- 事务管理:对关键操作使用事务保证数据一致性
- 类型安全:充分利用 TypeScript 类型系统
通过本文的学习,你已经掌握了在 Nest.js 中集成数据库的基本方法。无论是选择 TypeORM 还是 Prisma,都能帮助你高效地进行数据库操作。