TypeORMで1:1のリレーションの定義の仕方

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）](https://typeorm.io/#/migrations/generating-migrations)で生成します。


# 正解

[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\.](https://typeorm.io/#/one-to-one-relations)


## tsファイル

```TypeScript: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;
}
```

```TypeScript: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を書かない事です。

```TypeScript
  @OneToOne(
    () => EmployeeChild,
    (employeeChild) => employeeChild.employee,
  )
  @JoinColumn([{ name: 'id', referencedColumnName: 'employee_id' }])
  employeeChild: EmployeeChild;
```

とすると一見良さそうですが、

```
Referenced column employee_id was not found in entity EmployeeChild
```
というエラーが発生してうまくいきません。

## 生成されたmigrationファイル

```TypeScript
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`の方はいらないはずですが、消し方はわかりませんでした。（あってもパフォーマンスへの影響以外は無いと思われます）

```TypeScript
  @PrimaryColumn('int', { name: 'employee_id', unique: false })
```
としてみましたが結果は変わらず。



# 備考

TypeORM `0.2.25`
MySQL `5.7`

で確認しました。