簡體   English   中英

NestJS + TypeORM:使用兩個或更多數據庫?

[英]NestJS + TypeORM: Use two or more databases?

我從 2 天以來一直在嘗試解決這個問題,也許我只是錯過了這里的重點。

我的目標是編寫一個 NestJS 應用程序(包括 TypeORM),它為我的 2 或 3 個小項目提供 RestAPI,而不是為每個項目編寫一個 NestJS-App。

到目前為止一切順利,該應用程序已准備就緒,可以很好地與單個項目(它們與它們的實體、控制器、服務、模塊一起駐留在子文件夾中)一起運行,但我無法讓它與所有項目一起運行。

重點似乎是配置,我正在使用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 配置文件中定義

當然找不到“默認”,因為我提供了兩個具有與“默認”不同的唯一名稱的配置。

ApplicationModule中,我可以提供連接的名稱,如下所示:

TypeOrmModule.forRoot( { name: "project1" } ),

但它只適用於一個項目。

我可以將所有內容混合在一個配置中,但是然后我會將所有內容都放在一個數據庫中,所有用戶都使用相同的用戶,並且可能會混淆實體...

有人可以給我一個提示如何解決這個問題嗎? 也許每個模塊中都有getConnection(<name>) ,但是如何啟動 ApplicationModule 呢?

親切的問候,
薩傑羅伯特

我剛剛嘗試使用多個數據庫和一個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' },
  })
]

如果您使用多個數據庫連接,則需要在TypeOrmModule.forRoot({ name: 'db1Connection' })內顯式傳遞相同級別的連接名稱。

TypeOrmModule.forRootAsync({
  name: DB1_CONNECTION,
  imports: [ConfigModule],
  useClass: TypeormDb1ConfigService,
}),

TypeOrmModule.forRootAsync({
  name: DB2_CONNECTION,
  imports: [ConfigModule],
  useClass: TypeormDb2ConfigService,
})

為了清楚起見並讓其他開發人員來這篇文章:

來自NestJS 文檔

如果您沒有為連接設置任何名稱,則其名稱將設置為默認值。 請注意,您不應該有多個沒有名稱或名稱相同的連接,否則它們只會被覆蓋。

您的連接之一必須具有以下條件之一:

  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: [...]
})

在您的模塊中(不必是根模塊,只有您需要連接的模塊)。

對於那些面臨這個問題的人,這是我的解決方案

應用模塊

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [
        database,
        databaseAllo #<= my second database
      ]
    }),
    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)
  }

}

這就是我設法解決它的方法。 使用單個配置文件,我可以在應用程序 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 {}

包.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

在我偶然發現另一個問題之后,@DamarOwen 的回答對我有用:如果要將連接名稱存儲為變量,請不要從app.module.ts導出該常量 將變量存儲在另一個文件中,例如constants.ts


這是我嘗試過的:

app.module.ts

export const DB_CONNECTION_1 = 'conn1';

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      name: DB_CONNECTION_1,
      ...
    })
  ],
  ...
)
export class AppModule {}

數據庫.module.ts

@Module({
  imports: [
    TypeOrmModule.forFeature(
      [MyRepo],
      DB_CONNECTION_1,
    ),
  ],
  providers: [MyRepo],
})

我的.repo.ts

@Injectable()
export class MyRepo {
  constructor(
    @InjectRepository(MyOrmEntity, DB_CONNECTION_1)
    private readonly repo: Repository<MyOrmEntity>,
  ) {}
}

這引發了一個錯誤No repository found for was found. Looks like this entity is not registered in current "default" connection. was found. Looks like this entity is not registered in current "default" connection. (注意:我的應用程序有另一個名為“default”的連接)。

我不得不移動export const DB_CONNECTION_1 = 'conn1'; 遠離app.module.ts到它自己的文件constants.ts 然后它起作用了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM