【问题标题】:NestJS + TypeORM: Use two or more databases?NestJS + TypeORM:使用两个或更多数据库?
【发布时间】: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(&lt;name&gt;),但是如何启动ApplicationModule呢?

亲切的问候,
sagerobert

【问题讨论】:

    标签: javascript node.js nestjs typeorm


    【解决方案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 导入时,您是否还传递了连接的nameTypeOrmModule.forFeature([Album], 'albumsConnection')
    【解决方案2】:

    为了清楚起见并让其他开发者来这篇文章:

    来自NestJS documentation

    如果您没有为连接设置任何名称,则其名称将设置为默认值。请注意,您不应有多个没有名称或名称相同的连接,否则它们只会被覆盖。

    您的其中一个连接必须具有以下条件之一:

    1. "name":"default"
    2. 没有任何名字。

    我建议在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)
          }
        
        }
        

        【讨论】:

          猜你喜欢
          • 2020-04-25
          • 1970-01-01
          • 2019-05-09
          • 2022-07-22
          • 2020-04-19
          • 2023-02-03
          • 2021-05-26
          • 2021-06-05
          • 2021-05-03
          相关资源
          最近更新 更多