11-4 typeorm与nestjs集成
TypeORM集成基础
TypeORM与Prisma对比
Prisma优势详解
- 自动生成Repository
- 通过
prisma generate
命令自动生成类型安全的客户端 - 无需手动定义Repository接口
- 示例:自动生成
findUnique
、create
等方法
- 通过
- TypeScript支持
- 完整的类型推断和自动补全
- 数据库模型与TypeScript类型严格对应
- 示例:
const user = await prisma.user.findUnique({ where: { id: 1 } }); // user类型自动推断为User
typescript
- 简化数据库连接
- 内置连接池管理
- 支持多环境配置(开发/生产)
- 示例配置:
datasource db { provider = "postgresql" url = env("DATABASE_URL") }
prisma
TypeORM优势详解
- 关系型数据库兼容性
- 支持MySQL、PostgreSQL、SQLite、Oracle等
- 复杂查询优化(如联表查询)
- 示例:多对多关系定义
@Entity() export class Article { @ManyToMany(() => Tag) tags: Tag[]; }
typescript
- 灵活配置选项
- 支持Active Record和Data Mapper模式
- 自定义Repository扩展
- 示例:自定义Repository
@EntityRepository(User) export class UserRepository extends Repository<User> { findByName(name: string) { return this.find({ where: { name } }); } }
typescript
- 成熟生态
- 支持迁移(Migrations)
- 事务管理和查询缓存
- 示例:事务操作
await getManager().transaction(async (manager) => { await manager.save(user1); await manager.save(user2); });
typescript
💡提示:
- Prisma更适合快速原型开发
- TypeORM更适合需要深度定制和复杂查询的场景
- 两者都支持数据库迁移,但TypeORM的迁移工具更灵活
安装依赖
核心依赖说明
@nestjs/typeorm
- NestJS官方封装库
- 提供
TypeOrmModule
和装饰器(如@InjectRepository
) - 版本要求:与NestJS主版本匹配
typeorm
- 核心功能库
- 包含实体管理、查询构建器等功能
- 最新稳定版推荐(如
0.3.x
)
- 数据库驱动
- MySQL:
mysql2
(性能优于mysql
包) - PostgreSQL:
pg
- SQLite:
sqlite3
- 其他:参考TypeORM文档
- MySQL:
安装命令扩展
# 开发依赖(如类型定义)
npm install @types/node --save-dev
# 可选:数据库可视化工具(如DBeaver)
bash
版本兼容性
依赖项 | 推荐版本 | 备注 |
---|---|---|
@nestjs/typeorm | ^9.0.0 | 需匹配NestJS 8+ |
typeorm | ^0.3.0 | 注意Breaking Changes |
mysql2 | ^2.3.0 | 支持Promise和连接池 |
💡提示:
- 使用
npm outdated
检查版本更新 - 生产环境推荐固定版本号(如
0.3.12
)
快速验证安装
- 创建测试实体:
// src/user.entity.ts import { Entity, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; }
typescript - 配置模块:
// app.module.ts import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './user.entity'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'sqlite', database: ':memory:', entities: [User], synchronize: true, }), ], }) export class AppModule {}
typescript - 启动应用:
npm run start:dev
bash
若无报错,说明安装成功!
通过以上扩展,你应该对TypeORM的集成基础有了更全面的理解。接下来可以继续学习实体定义和复杂查询等内容! 🚀
配置与连接
基本配置详解
核心配置项说明
TypeOrmModule.forRoot({
type: 'mysql', // 数据库类型
host: 'localhost', // 数据库地址
port: 3306, // 端口号
username: 'root', // 用户名
password: 'root', // 密码
database: 'test', // 数据库名
entities: [User], // 实体类列表
synchronize: true, // 自动同步数据库结构
logging: true, // 启用SQL日志
poolSize: 10, // 连接池大小
connectTimeout: 30000, // 连接超时(毫秒)
})
typescript
关键配置项解析
synchronize
true
:应用启动时自动同步实体到数据库表结构- 仅限开发环境使用,生产环境必须设为
false
- 替代方案:使用迁移(Migrations)
entities
- 支持多种注册方式:
// 方式1:直接引用 entities: [User, Product] // 方式2:路径匹配 entities: [__dirname + '/**/*.entity{.ts,.js}']
typescript
- 支持多种注册方式:
- 数据库类型支持
类型 驱动包 备注 mysql mysql2 推荐 postgres pg sqlite sqlite3 适合本地开发 mssql mssql SQL Server
💡提示:生产环境务必配置连接池(poolSize
)和超时参数
动态配置进阶
异步配置最佳实践
TypeOrmModule.forRootAsync({
imports: [ConfigModule.forRoot({ isGlobal: true })], // 全局配置模块
useFactory: (config: ConfigService) => ({
type: config.get<'mysql'|'postgres'>('DB_TYPE'), // 泛型类型安全
host: config.get('DB_HOST'),
port: parseInt(config.get('DB_PORT'), 10), // 显式基数转换
username: config.get('DB_USER'),
password: config.get('DB_PASSWORD'),
database: config.get('DB_NAME'),
synchronize: config.get('NODE_ENV') !== 'prod', // 环境判断
autoLoadEntities: true,
extra: { // 高级配置
connectionLimit: 20,
ssl: config.get('DB_SSL') === 'true'
? { rejectUnauthorized: false }
: undefined
}
}),
inject: [ConfigService],
})
typescript
配置技巧
- 类型安全增强
// config/configuration.ts export default registerAs('database', () => ({ host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT, 10), type: process.env.DB_TYPE as 'mysql' | 'postgres' }));
typescript - 多环境配置示例
# .env.production DB_TYPE=postgres DB_SSL=true # .env.development DB_TYPE=mysql DB_SSL=false
ini - SSL连接配置
extra: { ssl: { ca: fs.readFileSync(__dirname + '/ca.crt'), rejectUnauthorized: false } }
typescript
错误处理方案
// src/core/database.provider.ts
export const typeormConfig = (config: ConfigService) => {
try {
return {
//...配置项,
retryAttempts: 3, // 重试次数
retryDelay: 3000, // 重试间隔(ms)
logger: new TypeORMLogger(config) // 自定义日志
};
} catch (err) {
throw new DatabaseConfigError(err.message);
}
};
typescript
配置验证工具
配置校验示例
// src/utils/config.validator.ts
import { plainToInstance } from 'class-transformer';
import { validateSync } from 'class-validator';
class DatabaseConfig {
@IsString()
DB_HOST: string;
@IsNumber()
DB_PORT: number;
}
const validatedConfig = validateSync(
plainToInstance(DatabaseConfig, process.env)
);
if (validatedConfig.length > 0) {
throw new Error(validatedConfig.toString());
}
typescript
可视化配置检查
常见问题解答
❓ Q: 为什么synchronize
在生产环境要禁用?
✅ 防止意外修改表结构导致数据丢失,应该使用迁移脚本
❓ Q: 如何处理数据库连接失败?
✅ 配置retryAttempts
和retryDelay
实现自动重连
❓ Q: 多数据库如何配置?
✅ 创建多个TypeOrmModule.forRoot()
连接,使用不同name区分
💡提示:使用TYPEORM_LOG=true
环境变量可输出详细调试日志
实体创建
User实体定义详解
// user.entity.ts
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
VersionColumn,
Index,
Unique
} from 'typeorm';
@Entity()
@Unique(['username']) // 用户名唯一约束
export class User {
@PrimaryGeneratedColumn('uuid') // 使用UUID作为主键
id: string;
@Column({ length: 50 })
@Index() // 为username字段创建索引
username: string;
@Column({
select: false, // 查询时默认不返回
length: 100
})
password: string;
@Column({ nullable: true }) // 允许为null
avatar: string;
@Column({ default: false }) // 默认值
isActive: boolean;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
@VersionColumn()
version: number; // 乐观锁
}
typescript
核心装饰器深度解析
@Entity()
- 可选参数:
@Entity('users') // 自定义表名 @Entity({ name: 'users', synchronize: false }) // 禁用同步
typescript - 继承实体:
@Entity() export class Admin extends User { @Column() privilegeLevel: number; }
typescript
- 可选参数:
@PrimaryGeneratedColumn()
- 主键策略:
@PrimaryGeneratedColumn() // 自增整数(默认) @PrimaryGeneratedColumn('uuid') // UUID @PrimaryGeneratedColumn('increment') // 序列(PostgreSQL)
typescript
- 主键策略:
@Column()
- 完整参数:
@Column({ type: 'varchar', // 字段类型 length: 100, // 长度限制 nullable: false, // 非空约束 default: 'guest', // 默认值 unique: true, // 唯一约束 comment: '用户昵称', // 字段注释 precision: 10, // 数字精度 scale: 2 // 小数位数 })
typescript
- 完整参数:
高级字段类型示例
// 枚举类型
@Column({ type: 'enum', enum: UserRole, default: UserRole.USER })
role: UserRole;
// JSON类型
@Column({ type: 'json' })
preferences: { theme: string; notifications: boolean };
// 数组类型(PostgreSQL)
@Column('text', { array: true })
tags: string[];
typescript
实体关系示例
// 一对多关系
@Entity()
export class Post {
@OneToMany(() => Comment, comment => comment.post)
comments: Comment[];
}
// 多对多关系
@Entity()
export class Product {
@ManyToMany(() => Category)
@JoinTable() // 仅在一方声明
categories: Category[];
}
typescript
实体生命周期钩子
@Entity()
export class User {
//...
@AfterInsert()
logInsert() {
console.log(`Inserted User: ${this.id}`);
}
@BeforeUpdate()
hashPassword() {
if (this.password) this.password = hash(this.password);
}
}
typescript
最佳实践建议
- 命名规范
- 实体类名:大驼峰(
UserProfile
) - 表名:小写蛇形(
user_profiles
) - 字段名:小写蛇形(
created_at
)
- 实体类名:大驼峰(
- 安全注意
// 敏感字段处理 @Column({ select: false }) password: string; // 虚拟字段 @Column({ select: false }) private _tempPassword: string; get tempPassword() { return decrypt(this._tempPassword); }
typescript - 性能优化
// 延迟关联加载 @ManyToOne(() => Group, { lazy: true }) group: Promise<Group>;
typescript
💡提示:使用typeorm-model-generator
可以从现有数据库自动生成实体类
通过以上扩展,你可以创建出功能完善、类型安全的TypeORM实体类,满足各种业务场景需求! 🚀
模块注册
AppModule 高级配置
import { Module, CacheModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User, Profile, Post } from './entities';
@Module({
imports: [
// 数据库核心配置(异步)
TypeOrmModule.forRootAsync({
useFactory: () => ({
type: 'mysql',
host: process.env.DB_HOST,
// ...其他配置
poolSize: 20,
extra: {
connectionTimeout: 30000,
// 连接池监控
acquireTimeout: 30000,
waitForConnections: true
}
})
}),
// 实体注册(支持多实体)
TypeOrmModule.forFeature([
User,
Profile,
Post
]),
// 缓存模块集成
CacheModule.register({
ttl: 60, // 缓存时间(秒)
max: 1000 // 最大缓存数
})
],
controllers: [UserController],
providers: [
UserService,
// 自定义Repository提供者
{
provide: 'USER_REPOSITORY_EXT',
useFactory: (connection: Connection) =>
connection.getCustomRepository(UserRepository),
inject: ['DATABASE_CONNECTION']
}
],
exports: [TypeOrmModule] // 导出以便其他模块复用
})
export class AppModule {}
typescript
关键方法深度解析
1. forRootAsync()
高级用法
- 配置工厂函数
TypeOrmModule.forRootAsync({ imports: [ConfigModule], useFactory: async (config: ConfigService) => ({ type: config.get('DB_TYPE'), // 动态加载实体文件 entities: await loadEntities(config.get('ENTITIES_PATH')), // 连接池健康检查 healthCheck: true, // 读写分离配置 replication: { master: { host: config.get('DB_MASTER') }, slaves: [ { host: config.get('DB_REPLICA1') }, { host: config.get('DB_REPLICA2') } ] } }), inject: [ConfigService] })
typescript - 多数据库连接
@Module({ imports: [ TypeOrmModule.forRootAsync({ name: 'userDB', // 用户数据库配置 }), TypeOrmModule.forRootAsync({ name: 'productDB', // 商品数据库配置 }) ] })
typescript
2. forFeature()
扩展功能
- 自定义Repository注册
TypeOrmModule.forFeature([ { name: 'USER_REPO', entity: User }, { name: 'PROFILE_REPO', entity: Profile } ])
typescript - 多数据库实体分配
TypeOrmModule.forFeature([User], 'userDB'), TypeOrmModule.forFeature([Product], 'productDB')
typescript
模块组织最佳实践
分层模块结构
// user.module.ts
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [UserService],
exports: [UserService]
})
export class UserModule {}
// app.module.ts
@Module({
imports: [
TypeOrmModule.forRootAsync(/*...*/),
UserModule,
ProductModule
]
})
export class AppModule {}
typescript
动态模块注册
// database.module.ts
@Module({})
export class DatabaseModule {
static forRoot(entities: any[]): DynamicModule {
return {
module: DatabaseModule,
imports: [
TypeOrmModule.forFeature(entities)
],
exports: [TypeOrmModule]
};
}
}
// 使用方式
@Module({
imports: [
DatabaseModule.forRoot([User, Product])
]
})
typescript
性能优化技巧
- 延迟加载模块
@Module({ imports: [ TypeOrmModule.forRootAsync({ useFactory: () => ({ // 启用延迟加载 lazyLoad: true, // 延迟加载阈值(ms) lazyLoadThreshold: 2000 }) }) ] })
typescript - 连接池监控
TypeOrmModule.forRoot({ // ... extra: { poolMonitor: { enable: true, frequency: 30000 // 监控频率(ms) } } })
typescript
常见问题解决方案
❓ Q: 如何实现多租户数据库切换?
✅ 方案:
// 租户感知的Repository
export class TenantAwareRepository {
constructor(
@InjectConnection()
private readonly connection: Connection
) {}
async switchDatabase(tenantId: string) {
const tenantConfig = getTenantConfig(tenantId);
this.connection.options = { ...this.connection.options, ...tenantConfig };
}
}
typescript
❓ Q: 如何测试TypeORM模块?
✅ 测试配置示例:
// test.module.ts
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
database: ':memory:',
entities: [User],
synchronize: true
})
]
})
export class TestModule {}
typescript
💡提示:使用@nestjs/testing
的Test.createTestingModule()
进行集成测试
通过这种模块化设计,你可以构建出灵活可扩展的数据库访问层! 🚀
控制器实现
Repository 注入与 CRUD 操作详解
import {
Controller,
Get,
Post,
Body,
Param,
Delete,
Patch,
Query
} from '@nestjs/common';
import {
InjectRepository,
Repository
} from 'typeorm';
import { User } from './user.entity';
@Controller('users')
export class UserController {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>
) {}
// 1. 查询所有用户(带分页)
@Get()
async findAll(
@Query('page') page = 1,
@Query('limit') limit = 10
): Promise<{ data: User[]; count: number }> {
const [data, count] = await this.userRepository.findAndCount({
skip: (page - 1) * limit,
take: limit,
order: { createdAt: 'DESC' }
});
return { data, count };
}
// 2. 查询单个用户
@Get(':id')
async findOne(@Param('id') id: string): Promise<User> {
return this.userRepository.findOne({
where: { id },
relations: ['profile'] // 加载关联数据
});
}
// 3. 创建用户
@Post()
async create(@Body() userData: Partial<User>): Promise<User> {
const user = this.userRepository.create(userData);
return this.userRepository.save(user);
}
// 4. 更新用户
@Patch(':id')
async update(
@Param('id') id: string,
@Body() updateData: Partial<User>
): Promise<User> {
await this.userRepository.update(id, updateData);
return this.userRepository.findOneBy({ id });
}
// 5. 删除用户
@Delete(':id')
async remove(@Param('id') id: string): Promise<void> {
await this.userRepository.softDelete(id); // 软删除
}
// 6. 自定义查询
@Get('search')
async search(@Query('keyword') keyword: string): Promise<User[]> {
return this.userRepository
.createQueryBuilder('user')
.where('user.username LIKE :keyword', { keyword: `%${keyword}%` })
.orWhere('user.email LIKE :keyword', { keyword: `%${keyword}%` })
.getMany();
}
}
typescript
核心功能扩展说明
1. @InjectRepository(Entity)
高级用法
- 自定义 Repository 注入
@InjectRepository(User, 'userDB') // 多数据库连接指定 private readonly userRepository: Repository<User>
typescript - 使用自定义 Repository 类
@InjectRepository(User) private readonly userRepository: UserRepository; // 继承自 Repository 的自定义类
typescript
2. 查询方法增强
方法 | 说明 | 示例 |
---|---|---|
find() | 基础查询 | find({ where: { isActive: true } }) |
findAndCount() | 分页查询(返回数据+总数) | findAndCount({ skip: 0, take: 10 }) |
findOneBy() | 条件查询单个记录 | findOneBy({ username: 'admin' }) |
createQueryBuilder() | 复杂查询构建器 | 见上方 search 方法示例 |
3. 数据操作进阶
- 批量操作
// 批量插入 await this.userRepository.insert([ { username: 'user1' }, { username: 'user2' } ]); // 批量更新 await this.userRepository.update( { isActive: false }, { status: 'inactive' } );
typescript - 事务处理
async transferFunds(senderId: string, receiverId: string, amount: number) { await this.userRepository.manager.transaction(async (manager) => { await manager.decrement(User, { id: senderId }, 'balance', amount); await manager.increment(User, { id: receiverId }, 'balance', amount); }); }
typescript
性能优化技巧
- 查询选择性加载
// 只选择需要的字段 find({ select: ['id', 'username'], where: { isActive: true } })
typescript - 缓存常用查询
@Get('active-users') @CacheTTL(60) // 缓存60秒 async getActiveUsers() { return this.userRepository.find({ where: { isActive: true }, cache: true }); }
typescript
安全最佳实践
- 输入验证
import { IsEmail, IsString } from 'class-validator'; class CreateUserDto { @IsString() username: string; @IsEmail() email: string; } @Post() async create(@Body() userData: CreateUserDto) { // ... }
typescript - 敏感数据处理
@Exclude() @Column({ select: false }) password: string;
typescript
错误处理示例
@Get(':id')
async findOne(@Param('id') id: string) {
const user = await this.userRepository.findOneBy({ id });
if (!user) {
throw new NotFoundException(`User ${id} not found`);
}
return user;
}
typescript
扩展功能实现
1. 文件上传关联
@Post(':id/avatar')
@UseInterceptors(FileInterceptor('file'))
async uploadAvatar(
@Param('id') id: string,
@UploadedFile() file: Express.Multer.File
) {
const avatarUrl = await uploadToS3(file);
await this.userRepository.update(id, { avatar: avatarUrl });
}
typescript
2. 数据导出端点
@Get('export/csv')
async exportToCsv(@Res() res: Response) {
const users = await this.userRepository.find();
const csv = convertToCsv(users);
res.header('Content-Type', 'text/csv');
res.attachment('users.csv');
return res.send(csv);
}
typescript
通过以上扩展,你的控制器将具备完整的 CRUD 功能、安全防护和性能优化! 🚀
常见问题解决
配置类型断言详解
类型断言最佳实践
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
type: 'mysql',
host: config.get('DB_HOST'),
port: parseInt(config.get('DB_PORT'), 10),
// 其他配置项...
// 使用类型断言解决复杂配置校验
extra: {
ssl: config.get('DB_SSL') === 'true'
? { rejectUnauthorized: false }
: undefined
}
}) as TypeOrmModuleOptions, // 显式类型断言
inject: [ConfigService]
})
typescript
类型安全增强方案
- 配置接口定义
interface DatabaseConfig { host: string; port: number; ssl?: { rejectUnauthorized: boolean }; } const config: DatabaseConfig = { host: process.env.DB_HOST!, port: parseInt(process.env.DB_PORT!, 10) };
typescript - 类验证器集成
class EnvConfig { @IsString() DB_HOST: string; @IsNumber() DB_PORT: number; }
typescript
环境变量高级管理
多环境配置方案
# .env.development
DB_SYNCHRONIZE=true
DB_LOGGING=true
# .env.production
DB_SYNCHRONIZE=false
DB_SSL=true
ini
动态加载实现
// config/configuration.ts
export default registerAs('database', () => ({
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT, 10),
ssl: process.env.NODE_ENV === 'production'
? { ca: fs.readFileSync('ca.pem') }
: undefined
}));
typescript
连接问题排查指南
常见错误处理
错误现象 | 解决方案 |
---|---|
连接超时 | 检查connectTimeout 配置,增加重试机制 |
SSL证书验证失败 | 配置extra.ssl.rejectUnauthorized=false (仅限开发环境) |
连接池耗尽 | 调整poolSize ,增加acquireTimeout |
实体未注册 | 检查entities 配置路径,使用autoLoadEntities: true |
健康检查实现
// health/database.health.ts
@Injectable()
export class DatabaseHealthIndicator extends HealthIndicator {
constructor(
@InjectConnection()
private readonly connection: Connection
) {}
async check() {
try {
await this.connection.query('SELECT 1');
return this.up('database');
} catch (e) {
return this.down('database', { error: e.message });
}
}
}
typescript
生产环境最佳实践
- 连接池配置模板
extra: { pool: { max: 20, min: 5, idleTimeoutMillis: 30000, acquireTimeoutMillis: 30000 } }
typescript - 监控集成
// 使用Prometheus监控 const metrics = new DatabaseMetrics(connection); setInterval(() => metrics.collect(), 15000);
typescript
自动化部署方案
扩展工具推荐
- 配置验证工具
npm install @nestjs/config class-validator class-transformer
bash - 数据库迁移工具
npx typeorm migration:generate -n InitSchema
bash - 环境管理工具
npm install dotenv-cli
bash
💡提示:使用NODE_ENV=test
时自动启用内存数据库进行测试
// test.config.ts
export const getTestConfig = () => ({
type: 'sqlite',
database: ':memory:',
synchronize: true,
logging: false
});
typescript
通过以上方案,你可以系统性地解决TypeORM集成中的各类配置问题! 🛠️
↑