2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【TypeORM】typescriptの開発時「synchronize: true」はEntityにもとづいてテーブルを自動生成してくれる。「synchronize: true」の強さの紹介と「synchronize: false」にする重要さの紹介

Last updated at Posted at 2021-09-07

記事概要

掲題の通り。
typeormが持つsynchronizeの機能を使えば、ヒトがテーブルを作成せずともスキーマさえあればテーブル定義を自動生成してくれる。

反面、テーブル定義変更にもとづく自動DROPが行われる場合もあるため、あくまでも開発最初期のスタートダッシュに留めるべき機能と考える。

こういう人向け

  • 筆者自身。「こういうのができましたよ」と後で自他に確認する。
  • typeormを他の人はどんな感じに書いてるのか知りたい人
  • CREATE TABLEを最初っから書くのが面倒で、entityを書けば自動生成してくれないかなと思ってる人

実行例

出力Entity

import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, Index } from "typeorm";

import { date_dictionary } from "./datadirectory"

// コードマスタ定義。
@Entity({ name: "m_code" })
// インデックスも張れる。ここでは大分類codeGroupIdと小分類codeIdに分けているイメージ。
@Index("codeid-pair" ,["codeGroupId", "codeId"], {  unique: true })
export class m_code extends BaseEntity {

  // primary keyやtype、サイズ、コメントも設定できる。
  @PrimaryGeneratedColumn({ name: "id", type: "bigint", comment: '自動採番ID' })
  id!: number;
  @Column({ nullable: false, type: "varchar", length: 7, comment: "グループコードID" })
  codeGroupId!: string;
  @Column({ nullable: false, type: "varchar", length: 7, comment: "コードID" })
  codeId!: string;
  @Column({ nullable: true, type: "varchar", length: 85, comment: "コード名" })
  codeName!: string;
  @Column({ nullable: false, type: "int", comment: "表示順" })
  hyoujiJyun!: number;
  // 共通カラム使用。デフォルトだとカラム名が連結される(dateTypeUpdatorみたいになる)ので、prefix:falseにしている
  @Column(() => date_dictionary, { prefix: false })
  dateType!: date_dictionary
}
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, UpdateDateColumn, CreateDateColumn } from "typeorm";

// @Entityが無い、テーブルとしては登録されないクラス。共通カラムを別々の場所で使う時に使える。
export class date_dictionary  {

    @Column({ nullable: true, type: "varchar", length: 12,comment: "作成者"  })
    creator!: string;
    @CreateDateColumn({ nullable: true, type: "datetime",comment: "作成日時"  })
    createdAt!: Date;
    @Column({ nullable: true, type: "varchar", length: 12 ,comment: "更新者" })
    updator!: string;
    @UpdateDateColumn({ nullable: true, type: "datetime",comment: "更新日時"  })
    updatedAt!: Date;
  }

接続設定


import dotenv from 'dotenv'
import { ConnectionOptions } from "typeorm";
import {Log4jsLogger} from "./common/logger"

// nuxt.js v14.12なので.envを有効活用している。
// 接続設定を環境変数process.env.~にすることで他環境へも適用できるようにする。
dotenv.config({ path: __dirname + '/.env' });

// https://typeorm.io/#/connection-options
const config: ConnectionOptions = {
    type: "mysql",
    host: process.env.CONNECT_HOST,
    port: Number(process.env.CONNECT_PORT),
    username: process.env.CONNECT_USER,
    password: process.env.CONNECT_PASS,
    database: process.env.CONNECT_DATABASE,
    // 指定したフォルダ内の@Entityを認識してくれる。
    // Nuxt.jsのservermiddlewareにて動かしてる扱いなので以下のようなフォルダ階層としている。models命名はsequelizeで開発してた時の名残
    entities: [
        "server/typeorm/models/*.*"
    ],
    // synchronize: trueの時、上記entityを正としてスキーマに接続、同期させる。
    // デバッグや開発時に役立つ。

    // 公式ドキュメントに書いてあるが、本番環境では絶対にfalseにしておくこと。
    synchronize: true,
    logging: true,
    // https://typeorm.io/#/logging
    // log4jsを用いてガチャガチャカスタムしてるので割愛。
    // Visual Code Studio + typescriptのクイックフィックスで編集必要なメソッドはわかる。
    logger:new Log4jsLogger(),
    timezone:"+09:00"
}
export default {
    config: config
}

接続時(なんとなく日時情報は抜いている)

[DEBUG] debug - START TRANSACTION
[DEBUG] debug - undefined
[DEBUG] debug - SELECT DATABASE() AS `db_name`
[DEBUG] debug - undefined
[DEBUG] debug - 
                        SELECT `TABLE_SCHEMA`,
                               `TABLE_NAME`
                        FROM `INFORMATION_SCHEMA`.`TABLES`
                        WHERE `TABLE_SCHEMA` = 'nuxt_typescript_training'
                          AND `TABLE_NAME` = 'm_code'
                    
[DEBUG] debug - undefined
[DEBUG] debug - SELECT * FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = 'nuxt_typescript_training' AND `TABLE_NAME` = 'typeorm_metadata'
[DEBUG] debug - undefined
[DEBUG] debug - SELECT SCHEMA() AS `schema_name`
[DEBUG] debug - undefined
[DEBUG] debug - CREATE TABLE `nuxt_typescript_training`.`m_code` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自動採番ID', `codeGroupId` varchar(7) NOT NULL COMMENT 'グループコードID', `codeId` varchar(7) NOT NULL COMMENT 'コードID', `codeName` varchar(85) NULL COMMENT 'コード名', `hyoujiJyun` int NOT NULL COMMENT '表示順', `creator` varchar(12) NULL COMMENT '作成者', `createdAt` datetime(6) NULL COMMENT '作成日時' DEFAULT CURRENT_TIMESTAMP(6), `updator` varchar(12) NULL COMMENT '更新者', `updatedAt` datetime(6) NULL COMMENT '更新日時' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `codeid-pair` (`codeGroupId`, `codeId`), PRIMARY KEY (`id`)) ENGINE=InnoDB
[DEBUG] debug - undefined
[DEBUG] debug - COMMIT

出来上がったテーブル

CREATE TABLE `m_code` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自動採番ID',
  `codeGroupId` varchar(7) NOT NULL COMMENT 'グループコードID',
  `codeId` varchar(7) NOT NULL COMMENT 'コードID',
  `codeName` varchar(85) DEFAULT NULL COMMENT 'コード名',
  `hyoujiJyun` int(11) NOT NULL COMMENT '表示順',
  `creator` varchar(12) DEFAULT NULL COMMENT '作成者',
  `createdAt` datetime(6) DEFAULT current_timestamp(6) COMMENT '作成日時',
  `updator` varchar(12) DEFAULT NULL COMMENT '更新者',
  `updatedAt` datetime(6) DEFAULT current_timestamp(6) ON UPDATE current_timestamp(6) COMMENT '更新日時',
  PRIMARY KEY (`id`),
  UNIQUE KEY `codeid-pair` (`codeGroupId`,`codeId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

自動生成されました!

けど、ちょっとテーブル定義を変更する。

// lengthを7→10に伸ばす。
  @Column({ nullable: false, type: "varchar", length: 10, comment: "グループコードID" })
  codeGroupId!: string;
  @Column({ nullable: false, type: "varchar", length: 10, comment: "コードID" })
  codeId!: string;

実行結果

[DEBUG] debug - START TRANSACTION
[DEBUG] debug - undefined
[DEBUG] debug - SELECT DATABASE() AS `db_name`
[DEBUG] debug - undefined
[DEBUG] debug - 
                        SELECT `TABLE_SCHEMA`,
                               `TABLE_NAME`
                        FROM `INFORMATION_SCHEMA`.`TABLES`
                        WHERE `TABLE_SCHEMA` = 'nuxt_typescript_training'
                          AND `TABLE_NAME` = 'm_code'
                    
[DEBUG] debug - undefined
[DEBUG] debug - 
                SELECT
                    *
                FROM
                    `INFORMATION_SCHEMA`.`COLUMNS`
                WHERE
                    `TABLE_SCHEMA` = 'nuxt_typescript_training'
                    AND
                    `TABLE_NAME` = 'm_code'
                
[DEBUG] debug - undefined
[DEBUG] debug - SELECT * FROM (
                SELECT
                    *
                FROM `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` `kcu`
                WHERE
                    `kcu`.`TABLE_SCHEMA` = 'nuxt_typescript_training'
                    AND
                    `kcu`.`TABLE_NAME` = 'm_code'
            ) `kcu` WHERE `CONSTRAINT_NAME` = 'PRIMARY'
[DEBUG] debug - undefined
[DEBUG] debug - 
            SELECT
                `SCHEMA_NAME`,
                `DEFAULT_CHARACTER_SET_NAME` as `CHARSET`,
                `DEFAULT_COLLATION_NAME` AS `COLLATION`
            FROM `INFORMATION_SCHEMA`.`SCHEMATA`
            
[DEBUG] debug - undefined
[DEBUG] debug - 
            SELECT
                `s`.*
            FROM (
                SELECT
                    *
                FROM `INFORMATION_SCHEMA`.`STATISTICS`
                WHERE
                    `TABLE_SCHEMA` = 'nuxt_typescript_training'
                    AND
                    `TABLE_NAME` = 'm_code'
            ) `s`
            LEFT JOIN (
                SELECT
                    *
                FROM `INFORMATION_SCHEMA`.`REFERENTIAL_CONSTRAINTS`
                WHERE
                    `CONSTRAINT_SCHEMA` = 'nuxt_typescript_training'
                    AND
                    `TABLE_NAME` = 'm_code'
            ) `rc`
                ON
                    `s`.`INDEX_NAME` = `rc`.`CONSTRAINT_NAME`
                    AND
                    `s`.`TABLE_SCHEMA` = `rc`.`CONSTRAINT_SCHEMA`
            WHERE
                `s`.`INDEX_NAME` != 'PRIMARY'
                AND
                `rc`.`CONSTRAINT_NAME` IS NULL
            
[DEBUG] debug - undefined
[DEBUG] debug - 
            SELECT
                `kcu`.`TABLE_SCHEMA`,
                `kcu`.`TABLE_NAME`,
                `kcu`.`CONSTRAINT_NAME`,
                `kcu`.`COLUMN_NAME`,
                `kcu`.`REFERENCED_TABLE_SCHEMA`,
                `kcu`.`REFERENCED_TABLE_NAME`,
                `kcu`.`REFERENCED_COLUMN_NAME`,
                `rc`.`DELETE_RULE` `ON_DELETE`,
                `rc`.`UPDATE_RULE` `ON_UPDATE`
            FROM (
                SELECT
                    *
                FROM `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` `kcu`
                WHERE
                    `kcu`.`TABLE_SCHEMA` = 'nuxt_typescript_training'
                    AND
                    `kcu`.`TABLE_NAME` = 'm_code'
            ) `kcu`
            INNER JOIN (
                SELECT
                    *
                FROM `INFORMATION_SCHEMA`.`REFERENTIAL_CONSTRAINTS`
                WHERE
                    `CONSTRAINT_SCHEMA` = 'nuxt_typescript_training'
                    AND
                    `TABLE_NAME` = 'm_code'
            ) `rc`
                ON
                    `rc`.`CONSTRAINT_SCHEMA` = `kcu`.`CONSTRAINT_SCHEMA`
                    AND
                    `rc`.`TABLE_NAME` = `kcu`.`TABLE_NAME`
                    AND
                    `rc`.`CONSTRAINT_NAME` = `kcu`.`CONSTRAINT_NAME`
            
[DEBUG] debug - undefined
[DEBUG] debug - SELECT VERSION() AS `version`
[DEBUG] debug - undefined
[DEBUG] debug - SELECT * FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = 'nuxt_typescript_training' AND `TABLE_NAME` = 'typeorm_metadata'
[DEBUG] debug - undefined
[DEBUG] debug - SELECT SCHEMA() AS `schema_name`
[DEBUG] debug - undefined
[DEBUG] debug - DROP INDEX `codeid-pair` ON `nuxt_typescript_training`.`m_code`
[DEBUG] debug - undefined
[DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` DROP COLUMN `codeGroupId`
[DEBUG] debug - undefined
[DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` ADD `codeGroupId` varchar(10) NOT NULL COMMENT 'グループコードID'
[DEBUG] debug - undefined
[DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` DROP COLUMN `codeId`
[DEBUG] debug - undefined
[DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` ADD `codeId` varchar(10) NOT NULL COMMENT 'コードID'
[DEBUG] debug - undefined
[DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` CHANGE `codeName` `codeName` varchar(85) NULL COMMENT 'コード名'
[DEBUG] debug - undefined
[DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` CHANGE `creator` `creator` varchar(12) NULL COMMENT '作成者'
[DEBUG] debug - undefined
[DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` CHANGE `updator` `updator` varchar(12) NULL COMMENT '更新者'
[DEBUG] debug - undefined
[DEBUG] debug - CREATE UNIQUE INDEX `codeid-pair` ON `nuxt_typescript_training`.`m_code` (`codeGroupId`, `codeId`)
[DEBUG] debug - undefined
[DEBUG] debug - COMMIT

ALTERで書き換えてくれた、けど…

[DEBUG] debug - DROP INDEX `codeid-pair` ON `nuxt_typescript_training`.`m_code`
[DEBUG] debug - undefined
[DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` DROP COLUMN `codeGroupId`
[DEBUG] debug - undefined
[DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` ADD `codeGroupId` varchar(10) NOT NULL COMMENT 'グループコードID'
[DEBUG] debug - undefined
[DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` DROP COLUMN `codeId`
[DEBUG] debug - undefined
[DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` ADD `codeId` varchar(10) NOT NULL COMMENT 'コードID'
[DEBUG] debug - undefined

###「synchronize: true」だとテーブル定義変更時は(少なくとも0.2.37段階のmysql対象だと)DROPしたあとに入れ直してくる!

当然新規作成されたレコードはnullないし初期値になるので、十分に留意する必要がある。
あと順序を考慮していないので、新しい列は最下部にドシドシ追加されていく形になる。

動作環境

https://typeorm.io/#/
上記公式ドキュメントの「Installation」を一読。
以下はあくまで2021年筆者の環境であるため、どこかの推奨というわけではない。

    "express": "^4.17.1",
    "mysql2": "^2.3.0",
    "nuxt": "<2.15.0",
    "reflect-metadata": "^0.1.13",
    "typeorm": "^0.2.37",

上記仕様を知った上での個人的考察

  • 開発初期に「synchronize: true」でガシガシ作る。この時にEntityをしっかり作り込む。
  • 出来上がったCREATE TABLEをDDLとして設計書に起こす。この時に「synchronize: false」にする
  • あとは必要に応じて人の手でALTER TABLEなりを行う。この時Migrationを使っても良いかもしれない。当然バックアップは取っておく。

上記の方のように、@Entity({name: 'users', synchronize: false})のようにsynchronizeを対処しても良いかもしれない。

終わりに

Entityを出発点とした自動データベース構築は個人的に非常に好ましいと考える。
自然とEntityとテーブル定義の内容が合致しているため、ありがちなカラム抜けやEntityの手抜きが激減する。
DBの細かい設定にも対応したtypeormのentityは今後も期待している。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?