6
1

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で1:1のリレーションの定義の仕方

Posted at

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)で生成します。

正解

TypeORM - Amazing ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL databases. Works in NodeJS, Browser, Ionic, Cordova and Electron platforms.

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 KEYUNIQ INDEXの両方が設定されています。

UNIQUE INDEXの方はいらないはずですが、消し方はわかりませんでした。(あってもパフォーマンスへの影響以外は無いと思われます)

  @PrimaryColumn('int', { name: 'employee_id', unique: false })

としてみましたが結果は変わらず。

備考

TypeORM 0.2.25
MySQL 5.7

で確認しました。

6
1
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
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?