TypeScriptのメジャーなORM(ORマッパー)であるTypeORMには、cliコマンド経由でmigrationファイルを自動生成する機能があります。
めちゃめちゃ便利なのですが、ドキュメントが不足気味のため、少し複雑なパターンの時に実装方法がわからない事があります。今回、1:1のリレーションの定義方法でハマったので、試行錯誤してわかったやり方を共有します。
やりたいこと
親テーブル(employee)
id | 備考 |
---|---|
id | pkey |
name |
子テーブル
id | 備考 |
---|---|
employee_id | pkey、employee.idへのfkey |
tag |
このような、
- 1:1のリレーション
- 子テーブルのpkeyが、親テーブルのpkeyへのfkeyになっている
というパターンをtypeORMのmigartion自動生成機能(generating-migarations)で生成します。
正解
tsファイル
employee.ts
import { Column, Entity, OneToOne, PrimaryGeneratedColumn } from 'typeorm';
import { EmployeeChild } from './employee-child.entity';
@Entity('employees')
export class Employee {
@PrimaryGeneratedColumn({ type: 'int', name: 'id' })
id: number;
@Column('varchar', { name: 'name', length: 255 })
name: string;
@OneToOne(
() => EmployeeChild,
(employeeChild) => employeeChild.employee,
)
employeeChild: EmployeeChild;
}
employee-child.ts
import { Column, Entity, JoinColumn, OneToOne, PrimaryColumn } from 'typeorm';
import { Employee } from './employee.entity';
@Entity('employee_children')
export class EmployeeChild {
@PrimaryColumn('int', { name: 'employee_id' })
@OneToOne(
() => Employee,
(employee) => employee.employeeChild,
)
@JoinColumn([{ name: 'employee_id', referencedColumnName: 'id' }])
employee: Employee;
@Column('varchar', { name: 'tag', length: 255 })
tag: string;
}
ポイントは、親テーブルの方にJoinColumnを書かない事です。
@OneToOne(
() => EmployeeChild,
(employeeChild) => employeeChild.employee,
)
@JoinColumn([{ name: 'id', referencedColumnName: 'employee_id' }])
employeeChild: EmployeeChild;
とすると一見良さそうですが、
Referenced column employee_id was not found in entity EmployeeChild
というエラーが発生してうまくいきません。
生成されたmigrationファイル
import { MigrationInterface, QueryRunner } from 'typeorm';
export class test1610269363257 implements MigrationInterface {
name = 'test1610269363257';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
'CREATE TABLE `employees` (`id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB',
undefined,
);
await queryRunner.query(
'CREATE TABLE `employee_children` (`employee_id` int NOT NULL, `tag` varchar(255) NOT NULL, UNIQUE INDEX `REL_379a2d987c438f3096c06a6ebd` (`employee_id`), PRIMARY KEY (`employee_id`)) ENGINE=InnoDB',
undefined,
);
await queryRunner.query(
'ALTER TABLE `employee_children` ADD CONSTRAINT `FK_379a2d987c438f3096c06a6ebd5` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION',
undefined,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
'ALTER TABLE `employee_children` DROP FOREIGN KEY `FK_379a2d987c438f3096c06a6ebd5`',
undefined,
);
await queryRunner.query(
'DROP INDEX `REL_379a2d987c438f3096c06a6ebd` ON `employee_children`',
undefined,
);
await queryRunner.query('DROP TABLE `employee_children`', undefined);
await queryRunner.query('DROP TABLE `employees`', undefined);
}
}
よく見るとemployee_id
に対して、PRIMARY KEY
とUNIQ INDEX
の両方が設定されています。
UNIQUE INDEX
の方はいらないはずですが、消し方はわかりませんでした。(あってもパフォーマンスへの影響以外は無いと思われます)
@PrimaryColumn('int', { name: 'employee_id', unique: false })
としてみましたが結果は変わらず。
備考
TypeORM 0.2.25
MySQL 5.7
で確認しました。