@nestjs/typeorm
是 NestJS 與 TypeORM 集成的官方模塊,提供了 forRoot()
和 forFeature()
兩個核心靜態方法用于配置數據庫連接和實體注冊。本文將深入解析這兩個方法的機制、使用場景和最佳實踐。
一、TypeOrmModule.forRoot()
- 全局數據庫配置
forRoot()
方法用于初始化全局數據庫連接,通常在應用的根模塊(如 AppModule
)中調用一次。
核心功能
- 創建數據庫連接
- 配置全局選項(如實體掃描路徑、遷移設置等)
- 注冊為全局模塊(可通過
@InjectConnection()
在任意地方注入)
基本用法
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: 'test',entities: [__dirname + '/**/*.entity{.ts,.js}'], // 自動掃描實體synchronize: true, // 開發環境自動同步實體(生產環境禁用)}),],
})
export class AppModule {}
高級配置選項
配置項 | 類型 | 說明 |
---|---|---|
type | 'mysql' | 'postgres' | 'sqlite' ... | 數據庫類型 |
entities | (string | Function)[] | 實體類或掃描路徑 |
synchronize | boolean | 自動同步實體結構(慎用) |
migrationsRun | boolean | 自動運行遷移 |
logging | boolean | ('query' | 'schema' | 'error' | 'warn' | 'info' | 'log')[] | SQL 日志 |
name | string | 多數據庫連接時的名稱標識 |
keepConnectionAlive | boolean | 應用關閉時保持連接 |
多數據庫連接
TypeOrmModule.forRoot({name: 'secondary',type: 'postgres',// ...其他配置
});
二、TypeOrmModule.forFeature()
- 模塊級實體注冊
forFeature()
方法用于在特定模塊中注冊實體和自定義 Repository,使它們僅在該模塊的作用域內可用。
核心功能
- 注冊實體(使它們可用于當前模塊的 Repository)
- 注冊自定義 Repository
- 支持多數據庫連接(通過
connectionName
指定)
基本用法
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserEntity } from './user.entity';
import { UserRepository } from './user.repository';@Module({imports: [TypeOrmModule.forFeature([UserEntity, UserRepository]),],
})
export class UserModule {}
關鍵特性解析
1. 實體注冊機制
- 自動注入依賴:注冊的實體可通過
@InjectRepository()
在服務中使用 - 作用域隔離:實體僅在當前模塊可用(除非全局注冊)
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserEntity } from './user.entity';@Injectable()
export class UserService {constructor(@InjectRepository(UserEntity)private userRepository: Repository<UserEntity>) {}
}
2. 自定義 Repository 支持
// user.repository.ts
import { EntityRepository, Repository } from 'typeorm';
import { UserEntity } from './user.entity';@EntityRepository(UserEntity)
export class UserRepository extends Repository<UserEntity> {findByName(name: string) {return this.findOne({ where: { name } });}
}// user.module.ts
TypeOrmModule.forFeature([UserRepository]); // 必須注冊自定義 Repository
3. 多數據庫連接支持
TypeOrmModule.forFeature([UserEntity], 'secondary' // 指定連接名稱
);
三、forRoot
與 forFeature
的協作機制
1. 初始化流程
- 應用啟動時,
forRoot()
創建全局數據庫連接 - 模塊加載時,
forFeature()
從全局連接中提取指定實體 - 動態生成包含實體和 Repository 的子模塊
2. 依賴注入關系
forRoot()
注冊的連接可通過@InjectConnection()
獲取forFeature()
注冊的 Repository 可通過@InjectRepository()
獲取
import { Injectable } from '@nestjs/common';
import { InjectConnection, InjectRepository } from '@nestjs/typeorm';
import { Connection, Repository } from 'typeorm';
import { UserEntity } from './user.entity';@Injectable()
export class DatabaseService {constructor(@InjectConnection() private connection: Connection,@InjectRepository(UserEntity) private userRepository: Repository<UserEntity>) {}
}
四、高級使用場景
1. 動態實體注冊
const entities = [UserEntity, ProductEntity]; // 可動態生成
TypeOrmModule.forFeature(entities);
2. 測試環境配置
TypeOrmModule.forRoot({type: 'sqlite',database: ':memory:',entities: [UserEntity],synchronize: true,
});
3. 混合使用全局和局部實體
// app.module.ts
TypeOrmModule.forRoot({entities: [SharedEntity], // 全局實體
});// feature.module.ts
TypeOrmModule.forFeature([LocalEntity]); // 局部實體
五、常見問題解決方案
1. RepositoryNotFoundError
- 原因:未在
forFeature()
中注冊實體 - 解決:確保使用實體的模塊已正確注冊
2. 多數據庫連接沖突
- 原因:未指定
connectionName
- 解決:
// 注冊時指定名稱 TypeOrmModule.forRoot({ name: 'secondary', ... });// 使用時指定連接 TypeOrmModule.forFeature([Entity], 'secondary');
3. 性能優化技巧
- 避免全局掃描:顯式指定實體而非使用通配符
// 不推薦(生產環境) entities: [__dirname + '/**/*.entity{.ts,.js}']// 推薦 entities: [UserEntity, ProductEntity]
六、最佳實踐總結
場景 | 推薦方案 |
---|---|
單數據庫應用 | 在根模塊使用一次 forRoot() ,按需在功能模塊使用 forFeature() |
多數據庫連接 | 為每個連接配置唯一的 name ,使用時顯式指定 |
自定義 Repository | 必須通過 forFeature() 注冊 |
測試環境 | 使用內存數據庫(如 SQLite) |
生產環境 | 禁用 synchronize ,使用遷移 |
七、完整示例
// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserModule } from './user/user.module';@Module({imports: [TypeOrmModule.forRoot({type: 'postgres',host: 'localhost',port: 5432,username: 'postgres',password: 'postgres',database: 'main',entities: [__dirname + '/**/*.entity{.ts,.js}'],synchronize: false,migrationsRun: true,migrations: [__dirname + '/migrations/**/*{.ts,.js}'],}),UserModule,],
})
export class AppModule {}// user.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserEntity } from './user.entity';
import { UserService } from './user.service';
import { UserRepository } from './user.repository';@Module({imports: [TypeOrmModule.forFeature([UserEntity, UserRepository]),],providers: [UserService],exports: [UserService],
})
export class UserModule {}
通過合理使用 forRoot
和 forFeature
,可以構建出既靈活又高效的數據庫訪問層架構。理解這兩個方法的協作機制是掌握 NestJS + TypeORM 集成的關鍵。