Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9360

【公式ドキュメント和訳】TypeORM Migration

$
0
0
この記事は、2021/10/10時点のTypeORMの仕様に依存します。 TypeORMのバージョンが違ったり、記事の公開から長い時間が経っている場合、正確ではない可能性があります。 typeorm: v0.2.38 翻訳元: migrations.md ライセンス: The MIT License 目次 マイグレーションとは マイグレーションを作成する マイグレーションを実行する マイグレーションを自動生成する コネクションオプション マイグレーションを記述するときに利用できるAPI マイグレーションとは 本番環境でもモデルの変更をデータベースに同期する必要があるわけですが、本番環境のデータベースにデータを入れた状態で、synchronize: trueを使うのは危険です。そこで、マイグレーションを使います。 マイグレーションの正体は普通のJSファイルで、SQLを使用してデータベースのスキーマを更新したり、既存のデータベースに変更を加えるというモノです。 では、すでにデータベースと、Postエンティティがあると仮定します。 import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class Post { @PrimaryGeneratedColumn() id: number; @Column() title: string; @Column() text: string; } このエンティティは、数ヶ月の間、本番環境で特に何も変更を加えられていませんでした。そして、その間に数千件の投稿がデータベースに保存されていました。 この状態で、titleというカラム名をnameに変更する必要が出てきました。どうすれば良いでしょうか。 正解は、以下のSQLを実行するマイグレーションを新規作成すれば良いです。(PostgreSQLの例) ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"; このSQLを実行すれば、データベースが更新されます。TypeORMはこのようなSQLを記述する 場所 を提供し、必要なときに実行できるようにします。この"場所"のことをマイグレーションと呼んでいます。 マイグレーションを作成する 事前にCLIのインストールが必要です。 マイグレーションを作成する前に、セットアップが必要です。 { "type": "mysql", "host": "localhost", "port": 3306, "username": "test", "password": "test", "database": "test", "entities": ["entity/*.js"], "migrationsTableName": "custom_migration_table", "migrations": ["migration/*.js"], "cli": { "migrationsDir": "migration" } } オプションの詳細は以下の通りです。 "migrationsTableName": "migrations" マイグレーションテーブルの名前をオプションのmigrations以外にする場合に指定します。 "migrations": ["migration/*.js"] マイグレーションの読み込み先のディレクトリを指定します。 "cli": { "migrationsDir": "migration" } CLIを利用して作成するマイグレーションの保存先ディレクトリを指定します。 セットアップが完了したら、CLIでマイグレーションを作成することができます。 typeorm migration:create -n PostRefactoring PostRefactoringというのがマイグレーションの名前を指していて、ここには好きな名前を指定することができます。このコマンドを実行すると、"migration"ディレクトリに{TIMESTAMP}-PostRefactoring.tsという形式のファイルが作成されます。{TIMESTAMP}の部分は、マイグレーションを作成した日時のタイムスタンプです。では、ファイルを開いて、SQLを書き込んでみましょう。 マイグレーションの中身は、以下のようになっているはずです。 import {MigrationInterface, QueryRunner} from "typeorm"; export class PostRefactoringTIMESTAMP implements MigrationInterface { async up(queryRunner: QueryRunner): Promise<void> { } async down(queryRunner: QueryRunner): Promise<void> { } } upとdownという二つのメソッドがあります。upはマイグレーションそのものを書くためのもので、downはupで行った変更を取り消すためのものです。 upもdownも、QueryRunnerオブジェクトを持っています。データベースに対する操作は、全てこのオブジェクトを介して行います。Query Runnerについてはこちらを参照してください。 Postを変更するマイグレーションは以下のようになります。 import {MigrationInterface, QueryRunner} from "typeorm"; export class PostRefactoringTIMESTAMP implements MigrationInterface { async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`ALTER TABLE "post" RENAME COLUMN "title" TO "name"`); } async down(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`ALTER TABLE "post" RENAME COLUMN "name" TO "title"`); // reverts things made in "up" method } } マイグレーションを実行する マイグレーションを作成したら、CLIコマンドで実行することができます。 typeorm migration:run typeorm migration:createかtypeorm migration:generateコマンドをoフラグを付けずに実行した場合、.tsファイルが生成されます。(詳しくは、Generating migrationsをご参照ください。)migration:runとmigration:revertコマンドでは、.jsファイルしか扱うことができません。従って、TypeScriptファイルは、マイグレーションの実行前にトランスパイルされている必要があります。他の方法としては、typeormをts-nodeと組み合わせて使用することで、.ts拡張子のマイグレーションファイルを実行することができます。 ts-nodeのサンプル ts-node --transpile-only ./node_modules/typeorm/cli.js migration:run ts-nodeのサンプル(node_modulesを参照しない場合) ts-node $(yarn bin typeorm) migration:run これらのコマンドは、保留中の全てのマイグレーションをタイムスタンプ順で実行します。言い換えると、作成したマイグレーションのupメソッド内に書いたSQLクエリが全て実行されるということです。これで、データベーススキーマを更新することができました。 マイグレーションを取り消すときは、以下のコマンドを実行します。 typeorm migration:revert このコマンドは、実行した中で最新のマイグレーションのdownを実行します。複数のマイグレーションを取り消す必要がある時は、このコマンドを複数回呼び出してください。 マイグレーションを自動生成する TypeORMでは、スキーマに対して行った変更から、自動的にマイグレーションを生成する機能があります。 titleカラムを持つPostエンティティがあったとして、titleをnameに変更した場合を想定しましょう。以下のコマンドを実行したとします。 typeorm migration:generate -n PostRefactoring すると、以下の内容を含む{TIMESTAMP}-PostRefactoring.tsの形式のファイルが自動生成されます。 import {MigrationInterface, QueryRunner} from "typeorm"; export class PostRefactoringTIMESTAMP implements MigrationInterface { async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`); } async down(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "name" RENAME TO "title"`); } } oフラグ(--outputJsのエイリアス)を使って、JavaScriptファイルを生成することもできます。TypeScriptのパッケージがインストールされていなくて、JavaScriptのみのプロジェクトの場合は便利です。その場合は、以下の内容を含んだ{TIMESTAMP}-PostRefactoring.jsファイルが生成されます。 const { MigrationInterface, QueryRunner } = require("typeorm"); module.exports = class PostRefactoringTIMESTAMP { async up(queryRunner) { await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`); } async down(queryRunner) { await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`); } } 自分でSQLクエリを書く必要がないというのは便利ですね。ただし、この方法では、モデルに変更を加えるたびにマイグレーションを生成してください。複数行のSQLクエリを含むマイグレーションを生成するには、pフラグ(--prettyのエイリアス)を使用してください。 コネクションオプション デフォルトではないコネクションに対してrunやrevertを実行する必要がある場合は、-cフラグ(--connectionのエイリアス)を使用して、コンフィグ名を引数として渡すことができます。 ここの説明はちょっと分かりにくいので、こっちを参照→Using ormconfig.json マイグレーションを記述するときに利用できるAPI APIを使ってデータベーススキーマを変更するときには、QueryRunnerを使用します。 サンプル import {MigrationInterface, QueryRunner, Table, TableIndex, TableColumn, TableForeignKey } from "typeorm"; export class QuestionRefactoringTIMESTAMP implements MigrationInterface { async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.createTable(new Table({ name: "question", columns: [ { name: "id", type: "int", isPrimary: true }, { name: "name", type: "varchar", } ] }), true) await queryRunner.createIndex("question", new TableIndex({ name: "IDX_QUESTION_NAME", columnNames: ["name"] })); await queryRunner.createTable(new Table({ name: "answer", columns: [ { name: "id", type: "int", isPrimary: true }, { name: "name", type: "varchar", }, { name: 'created_at', type: 'timestamp', default: 'now()' } ] }), true); await queryRunner.addColumn("answer", new TableColumn({ name: "questionId", type: "int" })); await queryRunner.createForeignKey("answer", new TableForeignKey({ columnNames: ["questionId"], referencedColumnNames: ["id"], referencedTableName: "question", onDelete: "CASCADE" })); } async down(queryRunner: QueryRunner): Promise<void> { const table = await queryRunner.getTable("answer"); const foreignKey = table.foreignKeys.find(fk => fk.columnNames.indexOf("questionId") !== -1); await queryRunner.dropForeignKey("answer", foreignKey); await queryRunner.dropColumn("answer", "questionId"); await queryRunner.dropTable("answer"); await queryRunner.dropIndex("question", "IDX_QUESTION_NAME"); await queryRunner.dropTable("question"); } } マイグレーションを記述するときに利用できるAPI getDatabases(): Promise<string[]> 全てのデータベース名を取得。 getSchemas(database?: string): Promise<string[]> database - データベースが指定されると、そのデータベースのスキーマを返す 全てのスキーマ名を取得。SQLServerとPostgreSQLでのみ使用可能。 getTable(tableName: string): Promise<Table|undefined> tableName - 読み込むテーブル名 指定した名前のテーブルを読み込む。 getTables(tableNames: string[]): Promise<Table[]> tableNames - 読み込むテーブル名(複数) 指定した名前のテーブルを読み込む(複数)。 hasDatabase(database: string): Promise<boolean> database - チェックするデータベース名 指定した名前のデータベースが存在するかをチェックする。 hasSchema(schema: string): Promise<boolean> schema - チェックするスキーマ名 指定した名前のスキーマが存在するかチェックする。SQLServerとPostgreSQLでのみ使用可能。 hasTable(table: Table|string): Promise<boolean> table - テーブルのオブジェクト、もしくは名前 テーブルが存在しているかチェックする。 hasColumn(table: Table|string, columnName: string): Promise<boolean> table - テーブルのオブジェクト、もしくは名前 columnName - チェックするカラムの名前 テーブルに指定したカラムが存在するかチェックする。 createDatabase(database: string, ifNotExist?: boolean): Promise<void> database - データベース名 ifNotExist - すでにデータベースが存在したとき、trueが指定されていればスキップするが、そうでなければ例外が投げられる 新しいデータベースを作成する。 dropDatabase(database: string, ifExist?: boolean): Promise<void> database - データベース名 ifExist - データベースが存在しなかったとき、trueが指定されていればスキップするが、そうでなければ例外が投げられる データベースを削除する。 createSchema(schemaPath: string, ifNotExist?: boolean): Promise<void> schemaPath - スキーマ名。SQLServerではスキーマパスをパラメータとして渡せる。スキーマパスを渡した場合、指定されたデータベースにスキーマが作成される。 ifNotExist - すでにスキーマが存在したとき、trueが指定されていればスキップするが、そうでなければ例外が投げられる 新しいテーブルスキーマを作成する。 dropSchema(schemaPath: string, ifExist?: boolean, isCascade?: boolean): Promise<void> schemaPath - スキーマ名。SQLServerではスキーマパスをパラメータとして渡せる。スキーマパスを渡した場合、指定されたデータベースにスキーマが削除される。 ifExist - データベースが存在しなかったとき、trueが指定されていればスキップするが、そうでなければ例外が投げられる isCascade - trueが指定された場合、スキーマに含まれるオブジェクト(テーブルや関数)も削除される。PostgreSQLでのみ指定可能。 テーブルスキーマを削除する。 createTable(table: Table, ifNotExist?: boolean, createForeignKeys?: boolean, createIndices?: boolean): Promise<void> table - テーブルオブジェクト。 ifNotExist - すでにスキーマが存在したとき、trueが指定されていればスキップするが、そうでなければ例外が投げられる createForeignKeys - テーブル作成時に外部キーを作成するかどうか(デフォルトはtrue) createIndices - テーブル作成時にインデックスを作成するかどうか(デフォルトはtrue) テーブルを作成する。 以降はAPIの詳細が続くので、原文を参照してください。

Viewing all articles
Browse latest Browse all 9360

Trending Articles