web-dev-qa-db-ja.com

TypeORMデータベースのシード方法

Typeorm ORMを使用してNode JSバックエンドを実行しています。

Entity Frameworkから来て、次のような数行でdbをシードするのは非常に簡単でした

Database.SetInitializer(new DbInitializer()); 

DbInitializerクラスには、すべてのシード情報が含まれます。

TypeOrmにデータベースをシードする同様のアプローチはありますか?そうでない場合、推奨される方法は何ですか?

1)データ挿入ステートメントを使用して新しい移行を作成しますか? 2)エンティティをインスタンス化して保存するタスクを作成しますか?

19
Aaron Ullal

私はそのような機能も見たいです(そして 私たちは一人ではありません )、しかし モーメント 、シードのための公式の機能はありません。

このような組み込み機能がない場合、次の最良の方法は0-Seedという名前の移行スクリプトを作成し(したがって、他の移行スクリプトよりも先に)、そこにシードデータを入力することです。

@ bitwit が作成されました スニペット これはあなたに役立つでしょう;これは、シード移行スクリプトに組み込むことができるyamlファイルからデータを読み取る関数です。

しかし、いくつかの調査の後、別の興味深いアプローチを見つけました:after_createイベントをテーブルにバインドし、リスナーで データを初期化 します。
私はこれを実装していないため、TypeORMで直接実行できるかどうかはわかりません。

7
Eliran Malka

残念ながら、 TypeORM (この回答が公開された時点)から公式にリリースされたソリューションはありません。

ただし、使用できる次善策があります。

  1. ormconfig.jsファイル内に別の接続を作成し、「移行」用に別のフォルダーを指定します-実際にはseeds
  2. -c <connection name>を使用してシードを生成および実行します。それでおしまい!

サンプルormconfig.js

module.exports = [
  {
    ...,
    migrations: [
      'src/migrations/*.ts'
    ],
    cli: {
      migrationsDir: 'src/migrations',
    }
  },
  {
    name: 'seed',
    ...,
    migrations: [
      'src/seeds/*.ts'
    ],
    cli: {
      migrationsDir: 'src/seeds',
    }
  }
]

サンプルpackage.json

{
  ...
  scripts: {
    "seed:generate": "ts-node typeorm migration:generate -c seed -n ",
    "seed:run": "ts-node typeorm migration:run -c seed",
    "seed:revert": "ts-node typeorm migration:revert -c seed",
  },
  ...
}
16
oleh.meleshko

TypeORMを Nest.js で使用している人向けに、コード内からプログラムでシードを実行するソリューションを紹介します。

大まかなアイデア:

  • シードを実行し、リクエストに応答する前にすべてのシードが行われるようにする「シードミドルウェア」を含む専用の「シードモジュール」を作成します。
  • リクエストが到着すると、シードミドルウェアはそれをインターセプトし、シードが完了したことが確認されるまで延期します。
  • Dbがシードされている場合、「シードミドルウェア」は要求を次のミドルウェアに渡します。
  • 速度を上げるために、「シードミドルウェア」はメモリ内の状態として「シード完了」フラグを保持し、シードが発生した後のさらなるdbチェックを回避します。

実装:

これが機能するためには、最初にすべての着信要求をリッスンするミドルウェアを登録するモジュールを作成します。

// file: src/seeding/SeedingModule.ts

@Module({})
export class SeedingModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(SeedingMiddleware)
      .forRoutes('*')
  }
}

次にミドルウェアを作成します。

// file: src/seeding/SeedingMiddleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';
import { EntityManager } from 'typeorm';
import { SeedingLogEntry } from './entities/SeedingLogEntry.entity';

@Injectable()
export class SeedingMiddleware implements NestMiddleware {

  // to avoid roundtrips to db we store the info about whether
  // the seeding has been completed as boolean flag in the middleware
  // we use a promise to avoid concurrency cases. Concurrency cases may
  // occur if other requests also trigger a seeding while it has already
  // been started by the first request. The promise can be used by other
  // requests to wait for the seeding to finish.
  private isSeedingComplete: Promise<boolean>;

  constructor(
    private readonly entityManager: EntityManager,
  ) {}

  async use(req: Request, res: Response, next: Function) {

    if (await this.isSeedingComplete) {
      // seeding has already taken place,
      // we can short-circuit to the next middleware
      return next();
    }

    this.isSeedingComplete = (async () => {
      // for example you start with an initial seeding entry called 'initial-seeding'
      // on 2019-06-27. if 'initial-seeding' already exists in db, then this
      // part is skipped
      if (!await this.entityManager.findOne(SeedingLogEntry, { id: 'initial-seeding' })) {
        await this.entityManager.transaction(async transactionalEntityManager => {
          await transactionalEntityManager.save(User, initialUsers);
          await transactionalEntityManager.save(Role, initialRoles);
          // persist in db that 'initial-seeding' is complete
          await transactionalEntityManager.save(new SeedingLogEntry('initial-seeding'));
        });
      }

      // now a month later on 2019-07-25 you add another seeding
      // entry called 'another-seeding-round' since you want to initialize
      // entities that you just created a month later
      // since 'initial-seeding' already exists it is skipped but 'another-seeding-round'
      // will be executed now.
      if (!await this.entityManager.findOne(SeedingLogEntry, { id: 'another-seeding-round' })) {
        await this.entityManager.transaction(async transactionalEntityManager => {
          await transactionalEntityManager.save(MyNewEntity, initalSeedingForNewEntity);
          // persist in db that 'another-seeding-round' is complete
          await transactionalEntityManager.save(new SeedingLogEntry('another-seeding-round'));
        });
      }

      return true;
    })();

    await this.isSeedingComplete;

    next();
  }
}

最後に、特定のタイプのシードが発生したことをデータベースに記録するために使用するエンティティを示します。 TypeOrmModule.forRoot呼び出しでエンティティとして登録してください。

// file: src/seeding/entities/Seeding.entity.ts

import { Entity, PrimaryColumn, CreateDateColumn } from 'typeorm';

@Entity()
export class Seeding {

  @PrimaryColumn()
  public id: string;

  @CreateDateColumn()
  creationDate: Date;

  constructor(id?: string) {
    this.id = id;
  }
}

ライフサイクルイベントを使用した代替シードソリューション:

nest.jsでは、シードを処理するミドルウェアベースのソリューションを使用する代わりに、OnApplicationBootstrapインターフェイス( lifecycle events を参照)を実装することもできます。 onApplicationBootstrapメソッドは、「アプリケーションが完全に起動し、ブートストラップされると呼び出されます」。ただし、このアプローチでは、ミドルウェアソリューションとは異なり、実行時に異なるテナントのdbスキーマが作成され、実行時にシードを数回実行する必要があるマルチテナント環境にdbをシードできません。作成後の異なるテナント。

1
B12Toaster