NestJS + TypeORM:使用两个或更多数据库?
Posted
技术标签:
【中文标题】NestJS + TypeORM:使用两个或更多数据库?【英文标题】:NestJS + TypeORM: Use two or more databases? 【发布时间】:2019-01-30 08:22:47 【问题描述】:我从 2 天以来一直在尝试解决这个问题,也许我只是错过了这里的重点。
我的目标是编写一个 NestJS 应用程序(包括 TypeORM),它为我的 2 或 3 个小项目提供 RestAPI,而不是为每个项目编写一个 NestJS 应用程序。
到目前为止一切顺利,该应用程序已准备就绪,可与单个项目(与实体、控制器、服务、模块一起位于子文件夹中)很好地工作,但我无法让它与所有项目一起运行。
重点好像是配置,我用的是ormconfig.json
:
[
"name": "Project1",
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "<username>",
"password": "<pwd>",
"database": "<database>",
"synchronize": false,
"entities": ["project1/*.entity.ts"],
"subscribers": ["project1/*.subscriber.ts"],
"migrations": ["project1/migrations/*.ts"],
"cli": "migrationsDir": "project1/migrations"
,
"name": "project2",
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "<another-username>",
"password": "<another-pwd>",
"database": "<another-database>",
"synchronize": false,
"entities": ["project2/*.entity.ts"],
"subscribers": ["project2/*.subscriber.ts"],
"migrations": ["project2/migrations/*.ts"],
"cli": "migrationsDir": "project2/migrations"
]
错误信息说:
[ExceptionHandler] 找不到连接默认值,因为它没有在任何 orm 配置文件中定义
当然找不到“default”,因为我提供了两个具有不同于“default”的唯一名称的配置。
在 ApplicationModule 中,我可以提供连接的名称,如下所示:
TypeOrmModule.forRoot( name: "project1" ),
但它只适用于一个项目。
我可以在一个配置中混合所有内容,但是我会将所有内容都放在一个数据库中,所有用户都使用相同的用户,并且可能会混淆实体...
谁能给我一个提示如何解决这个问题?
可能在每个模块中都有getConnection(<name>)
,但是如何启动ApplicationModule呢?
亲切的问候, sagerobert
【问题讨论】:
【参考方案1】:我刚刚尝试使用多个数据库和 ormconfig.json
设置 TypeORM,但它根本不适合我。它似乎总是使用default
连接,当没有找到默认(= 没有明确名称)连接时,它会抛出相应的错误。
当我在 app.module.ts
中定义连接时,它确实有效(我删除了 ormconfig.json
):
imports: [
...,
TypeOrmModule.forRoot(
name: 'Project1',
type: 'mysql',
host: 'localhost',
port: 3306,
username: '<username>',
password: '<pwd>',
database: '<database>',
synchronize: false,
entities: ['project1/*.entity.ts'],
subscribers: ['project1/*.subscriber.ts'],
migrations: ['project1/migrations/*.ts'],
cli: migrationsDir: 'project1/migrations' ,
),
TypeOrmModule.forRoot(
name: 'project2',
type: 'mysql',
host: 'localhost',
port: 3306,
username: '<another-username>',
password: '<another-pwd>',
database: '<another-database>',
synchronize: false,
entities: ['project2/*.entity.ts'],
subscribers: ['project2/*.subscriber.ts'],
migrations: ['project2/migrations/*.ts'],
cli: migrationsDir: 'project2/migrations' ,
)
]
【讨论】:
非常感谢,这似乎解决了这个问题!又一个问题:在此块之前,在...,
部分中,您是为每个“项目”(如 Project1Module, Project2Module
)导入模块还是另外导入其他内容?
抱歉,错过了您的评论。不,我想不出任何特殊的进口。
出于兴趣...您是如何运行迁移的?
按照你说的定义,我没有得到任何错误。但程序始终默认运行数据库。知道还应该检查什么吗?
@SorayaAnvari 当您使用forFeature
导入时,您是否还传递了连接的name
:TypeOrmModule.forFeature([Album], 'albumsConnection')
?【参考方案2】:
为了清楚起见并让其他开发者来这篇文章:
来自NestJS documentation:
如果您没有为连接设置任何名称,则其名称将设置为默认值。请注意,您不应有多个没有名称或名称相同的连接,否则它们只会被覆盖。
您的其中一个连接必须具有以下条件之一:
"name":"default"
没有任何名字。
我建议在ormconfig.json
中声明所有连接,而不是在代码中声明。
从ormconfig.json
导入连接的示例:
@Module(
imports: [TypeOrmModule.forFeature([Entity1, Entity2]), //This will use default connection
TypeOrmModule.forRoot(name: 'con1'), // This will register globaly con1
TypeOrmModule.forRoot(name: 'con2'), // This will register globaly con2
controllers: [...],
providers: [...],
exports: [...]
)
在您的模块中(不必是根模块,只有您需要连接的模块)。
【讨论】:
【参考方案3】:如果您使用多个数据库连接,则需要在 TypeOrmModule.forRoot( name: 'db1Connection' ) 内显式传递同一级别的连接名称。
TypeOrmModule.forRootAsync(
name: DB1_CONNECTION,
imports: [ConfigModule],
useClass: TypeormDb1ConfigService,
),
TypeOrmModule.forRootAsync(
name: DB2_CONNECTION,
imports: [ConfigModule],
useClass: TypeormDb2ConfigService,
)
【讨论】:
先生,您救了我的命。TypeOrmModule.forRootAsync
拥有我见过的最糟糕的 API 接口。【参考方案4】:
这就是我设法解决的方法。使用单个配置文件,我可以在应用程序 boostrap 或使用 TypeOrm 的 CLI 上运行迁移。
src/config/ormconfig.ts
import parseBoolean from '@eturino/ts-parse-boolean';
import TypeOrmModuleOptions from '@nestjs/typeorm';
import * as dotenv from 'dotenv';
import join from 'path';
dotenv.config();
export = [
//name: 'default',
type: 'mssql',
host: process.env.DEFAULT_DB_HOST,
username: process.env.DEFAULT_DB_USERNAME,
password: process.env.DEFAULT_DB_PASSWORD,
database: process.env.DEFAULT_DB_NAME,
options:
instanceName: process.env.DEFAULT_DB_INSTANCE,
enableArithAbort: false,
,
logging: parseBoolean(process.env.DEFAULT_DB_LOGGING),
dropSchema: false,
synchronize: false,
migrationsRun: parseBoolean(process.env.DEFAULT_DB_RUN_MIGRATIONS),
migrations: [join(__dirname, '..', 'model/migration/*.ts,js')],
cli:
migrationsDir: 'src/model/migration',
,
entities: [
join(__dirname, '..', 'model/entity/default/**/*.entity.ts,js'),
],
as TypeOrmModuleOptions,
name: 'other',
type: 'mssql',
host: process.env.OTHER_DB_HOST,
username: process.env.OTHER_DB_USERNAME,
password: process.env.OTHER_DB_PASSWORD,
database: process.env.OTHER_DB_NAME,
options:
instanceName: process.env.OTHER_DB_INSTANCE,
enableArithAbort: false,
,
logging: parseBoolean(process.env.OTHER_DB_LOGGING),
dropSchema: false,
synchronize: false,
migrationsRun: false,
entities: [],
as TypeOrmModuleOptions,
];
src/app.module.ts
import configuration from '@config/configuration';
import validationSchema from '@config/validation';
import Module from '@nestjs/common';
import ConfigModule from '@nestjs/config';
import TypeOrmModule from '@nestjs/typeorm';
import LoggerService from '@shared/logger/logger.service';
import UsersModule from '@user/user.module';
import AppController from './app.controller';
import ormconfig = require('./config/ormconfig'); //path mapping doesn't work here
@Module(
imports: [
ConfigModule.forRoot(
cache: true,
isGlobal: true,
validationSchema: validationSchema,
load: [configuration],
),
TypeOrmModule.forRoot(ormconfig[0]), //default
TypeOrmModule.forRoot(ormconfig[1]), //other db
LoggerService,
UsersModule,
],
controllers: [AppController],
)
export class AppModule
package.json
"scripts":
...
"typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js --config ./src/config/ormconfig.ts",
"typeorm:migration:generate": "npm run typeorm -- migration:generate -n",
"typeorm:migration:run": "npm run typeorm -- migration:run"
,
项目结构
src/
├── app.controller.ts
├── app.module.ts
├── config
│ ├── configuration.ts
│ ├── ormconfig.ts
│ └── validation.ts
├── main.ts
├── model
│ ├── entity
│ ├── migration
│ └── repository
├── route
│ └── user
└── shared
└── logger
【讨论】:
【参考方案5】:对于那些面临这个问题的人,这是我的解决方案
应用模块
@Module(
imports: [
ConfigModule.forRoot(
isGlobal: true,
load: [
database,
databaseAllo
]
),
TypeOrmModule.forRootAsync(
useFactory: (configs: ConfigService) => configs.get("db_config"),
inject: [ConfigService],
),
TypeOrmModule.forRootAsync(
name:"db_allo", <= create connection to my second db
useFactory: (configs: ConfigService) => configs.get("db_config_allo"),
inject: [ConfigService],
),
AuthModule,
JwtAuthModule
],
controllers: []
)
export class AppModule
我的项目模块(包含来自第二个数据库的表)
@Module(
imports: [
TypeOrmModule.forFeature([AlloMpcTable], "db_allo" <= call connection again),
],
providers: [
AlloRepository
],
exports: [AlloRepository],
controllers: [],
)
export class AlloModule
我的项目仓库
@Injectable()
export class AlloRepository extends BaseRepository<AlloMpcTable>
constructor(
@InjectRepository(AlloMpcTable, "db_allo") <= you need to call connection again
private readonly allo: Repository<AlloMpcTable>,
)
super(allo)
public async Find(id: number): Promise<AlloMpcTable>
return await this.allo.findOne(id)
【讨论】:
以上是关于NestJS + TypeORM:使用两个或更多数据库?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 typeORM 在 Nestjs 中创建类型为 Date 的列并键入 DateTime?
TypeOrm - NestJS 使用 queryBuilder
TypeError:存储库方法不是函数(NestJS / TypeORM)