连接数据库
在实际项目中,数据的持久化和管理是后端开发的核心环节。NestJS 推荐使用 TypeORM 作为 ORM 框架,支持多种主流数据库(如 MySQL、PostgreSQL、SQLite 等),并与 NestJS 的依赖注入、模块化机制深度集成。
通过 ORM,开发者可以用面向对象的方式操作数据库,避免了繁琐的 SQL 拼接和手动管理连接池等底层细节。TypeORM 让你专注于业务逻辑,提升开发效率和代码可维护性。
TypeORM 简介
TypeORM 是一款基于 TypeScript 的 ORM 框架,支持实体(Entity)与数据库表的映射,提供声明式的数据库操作方式。它让开发者可以像操作对象一样操作数据库,极大提升开发效率和代码可维护性。
- 支持多种数据库:MySQL、PostgreSQL、SQLite、MongoDB 等
- 支持实体、关系、迁移、查询构建器等高级特性
- 与 NestJS 无缝集成,支持依赖注入和模块化
TypeORM 的核心思想是"实体即表",每个实体类对应数据库中的一张表,类的属性对应表的字段。通过装饰器声明字段类型、主键、索引、关系等,代码即文档,结构一目了然。
环境准备与数据库配置
- 安装依赖:
npm install --save @nestjs/typeorm typeorm mysql2
- 在
app.module.ts中配置 TypeORM:
// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserModule } from './user/user.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'your_password',
database: 'test_db',
autoLoadEntities: true, // 自动加载实体
synchronize: true, // 开发环境下自动建表,生产环境建议关闭
}),
UserModule,
],
})
export class AppModule {}
synchronize: true适合开发阶段,生产环境建议用迁移工具管理表结构。
定义实体(Entity)
实体是 TypeORM 中用于描述数据库表结构的类。每个实体对应一张表,类的属性对应表的字段。通过装饰器可以声明主键、唯一约束、默认值、关系等。
// user/user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { Post } from '../post/post.entity';
@Entity('user')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 20 })
username: string;
@Column()
password: string;
@Column({ default: true })
isActive: boolean;
@OneToMany(() => Post, post => post.user)
posts: Post[]; // 用户的所有文章
}
通过关系装饰器(如 @OneToMany、@ManyToOne)可以轻松实现连表查询。
Repository 的作用
TypeORM 的 Repository 是操作实体的核心对象,封装了常用的增删改查方法(如 save、find、findOne、update、delete 等),也支持复杂的查询构建。
在 NestJS 中,Repository 通过 @InjectRepository 注入到 Service 层,结合依赖注入机制,保证了代码的解耦和可测试性。
集成 TypeORM 模块
在业务模块中引入 TypeORM 的 TypeOrmModule.forFeature,并在 Service 层注入 Repository 进行数据库操作。
// user/user.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UserService } from './user.service';
import { UserController } from './user.controller';
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [UserService],
controllers: [UserController],
exports: [UserService],
})
export class UserModule {}
实现基础 CRUD 操作
// user/user.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private readonly userRepo: Repository<User>,
) {}
async create(username: string, password: string) {
const user = this.userRepo.create({ username, password });
return this.userRepo.save(user);
}
async findAll() {
return this.userRepo.find();
}
async findById(id: number) {
return this.userRepo.findOneBy({ id });
}
async update(id: number, data: Partial<User>) {
await this.userRepo.update(id, data);
return this.findById(id);
}
async remove(id: number) {
return this.userRepo.delete(id);
}
}
连表查询(关系与 QueryBuilder)
在实际业务中,常常需要查询用户及其关联的其他数据(如文章、评论等)。TypeORM 支持通过 relations 选项或 QueryBuilder 实现连表查询。
1. 使用 relations 选项
// 查询用户及其所有文章
async findUserWithPosts(id: number) {
return this.userRepo.findOne({
where: { id },
relations: ['posts'], // 关联查询
});
}
2. 使用 QueryBuilder
QueryBuilder 提供了更灵活的 SQL 构建能力,适合复杂的多表关联、条件筛选等场景。
import { getRepository } from 'typeorm';
async function findActiveUsersWithPosts() {
return getRepository(User)
.createQueryBuilder('user')
.leftJoinAndSelect('user.posts', 'post')
.where('user.isActive = :active', { active: true })
.getMany();
}
事务(Transaction)处理
在涉及多步数据库操作时,事务可以保证数据的一致性和原子性。TypeORM 支持多种事务写法。
1. 使用 manager 进行手动事务
import { Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
@Injectable()
export class UserService {
constructor(private dataSource: DataSource) {}
async transferPoints(fromUserId: number, toUserId: number, points: number) {
return this.dataSource.transaction(async manager => {
const fromUser = await manager.findOne(User, { where: { id: fromUserId } });
const toUser = await manager.findOne(User, { where: { id: toUserId } });
if (!fromUser || !toUser) throw new Error('用户不存在');
if (fromUser.points < points) throw new Error('积分不足');
fromUser.points -= points;
toUser.points += points;
await manager.save(fromUser);
await manager.save(toUser);
});
}
}
2. 使用 @Transaction 装饰器(适用于较老版本 TypeORM)
推荐优先使用 DataSource 的 transaction 方法,@Transaction 装饰器已不再推荐。
Controller 示例
// user/user.controller.ts
import { Controller, Post, Body, Get, Param, Put, Delete } from '@nestjs/common';
import { UserService } from './user.service';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post('register')
async register(@Body() body: { username: string; password: string }) {
return this.userService.create(body.username, body.password);
}
@Get(':id')
async getUser(@Param('id') id: number) {
return this.userService.findById(id);
}
@Get()
async list() {
return this.userService.findAll();
}
@Put(':id')
async update(@Param('id') id: number, @Body() body: any) {
return this.userService.update(id, body);
}
@Delete(':id')
async remove(@Param('id') id: number) {
return this.userService.remove(id);
}
}
实战场景:用户信息的持久化
通过上述结构,用户注册、查询、更新、删除等操作都可以通过 RESTful API 实现,并自动映射到数据库表。这样不仅提升了开发效率,也保证了数据一致性和安全性。
在实际项目中,常见的业务场景还包括:
- 用户与订单、文章等多表关联的查询与统计
- 复杂的筛选、分页、排序等需求
- 需要保证一致性的积分转账、余额扣减等事务操作
TypeORM 的丰富特性可以帮助你优雅地应对这些需求。
最佳实践与常见坑点
- 实体字段类型要与数据库一致,否则可能导致运行时错误。
- 生产环境关闭 synchronize,使用 TypeORM migration 管理表结构。
- Repository 注入要用 @InjectRepository,否则依赖注入会失败。
- DTO 校验与实体分离,避免直接暴露数据库结构。
- 错误处理要完善,如用户不存在时返回 404。
- 避免 N+1 查询,可用
relations或 QueryBuilder 优化。 - 事务操作要捕获异常,保证回滚,避免数据不一致。
- 建议统一管理数据库连接配置,便于多环境切换和维护。
连接数据库是后端开发的基础,建议多查阅 TypeORM 官方文档 和 NestJS 数据库集成文档,掌握更多高级用法。