前置知识回顾
在前面的学习中,我们已经完成了一个基础的 NestJS 模板项目,重点搭建了 common 模块中的 Logger 模块(日志)和 Config 模块(配置)。这两个模块后续还会持续扩展。
接下来我们进入了 ORM 库对接数据库的学习。涉及两大类数据库与对应 ORM 方案:
| 数据库类型 | ORM 库 | 数据库产品 |
|---|---|---|
| 关系型数据库 | TypeORM / Prisma | MySQL、PostgreSQL |
| 非关系型数据库 | Mongoose | MongoDB |
此前我们仅实现了基础的 ORM 连接示例,并未深入权限控制等高级话题。本节的重点是从基础的单库连接,进阶到多数据库连接场景。
多租户与数据库架构
在更高层次上,不同用户可能属于不同的组织或租户(Tenant),租户标识可以是一个企业、团体或项目。相比于在单库中通过表字段逻辑隔离数据,多租户方案通过不同的 ORM 实例连接不同的物理数据库,实现了更彻底的数据隔离:
用户请求 → 租户标识(Tenant ID)→ ORM 实例 1 → 数据库 A
→ ORM 实例 2 → 数据库 B
→ ORM 实例 3 → 数据库 C
text
这种架构的优势在于数据隔离彻底、安全性更高,但维护复杂度也相应增加。当我们把这种复杂场景掌握后,简单场景自然不在话下。
NestJS 多数据库配置方案
NestJS 官方文档的 Techniques > Database > Multiple Databases 章节提供了 TypeORM 多数据库连接的标准配置方式。核心要点如下:
1. 使用 name 属性区分连接
在 TypeOrmModule.forRoot() 中配置多个数据库时,必须通过 name 属性区分不同连接。如果没有设置 name,后续的连接配置会覆盖前面的连接,并且会报错:
const defaultOptions = {
type: 'mysql',
port: 3306,
username: 'root',
password: 'example',
database: 'test_db',
synchronize: true,
};
@Module({
imports: [
// 默认数据库连接(无需 name)
TypeOrmModule.forRoot({
...defaultOptions,
host: 'db',
entities: [User],
}),
// 第二个数据库连接(必须设置 name)
TypeOrmModule.forRoot({
...defaultOptions,
name: 'mysql1',
host: 'db1',
port: 3307,
entities: [User],
}),
],
})
export class AppModule {}
typescript
2. 在 Feature 模块中指定连接
使用 TypeOrmModule.forFeature() 注册实体时,需要传递对应的连接名称,否则 TypeORM 无法确定将实体注册到哪个数据库:
@Module({
imports: [
// 注册到默认数据库
TypeOrmModule.forFeature([User]),
// 注册到 mysql1 数据库
TypeOrmModule.forFeature([User], 'mysql1'),
],
})
export class AppModule {}
typescript
3. 通过依赖注入使用指定连接
在 Service 或 Controller 中,使用 @InjectDataSource() 或 @InjectRepository() 装饰器指定要注入的数据库连接:
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
@Injectable()
export class AppService {
constructor(
// 默认数据库的 Repository
@InjectRepository(User)
private userRepository: Repository<User>,
// mysql1 数据库的 Repository
@InjectRepository(User, 'mysql1')
private userRepository1: Repository<User>,
) {}
}
typescript
也可以直接注入 DataSource 对象获取更底层的控制能力:
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
@Injectable()
export class AlbumsService {
constructor(
@InjectDataSource('albumsConnection')
private dataSource: DataSource,
) {}
}
typescript
小结
- 多数据库配置的核心在于
name属性的正确使用,确保每个连接都有唯一标识。 forFeature()中必须传入对应的连接名称,才能将实体注册到正确的数据库。- 通过 NestJS 的依赖注入系统,可以在 Service 层灵活选择使用哪个数据库的 Repository。
- 这种方案为后续实现多租户架构奠定了基础。
↑