🐕

typeormに中間テーブルを挿入する方法(ManyToManyを使わない)

2021/11/18に公開

概要

typeorm で中間テーブルを手動で作成する方法についてまとめます。
ManyToMany が自動生成されるので、あまり意味がなかったりしますが、気になったので調べました。

ソースコードは以下になります。
https://github.com/Msksgm/typeorm-manual-junction-table

実装方法

typeorm、docker-compose を使います。
若干異なりますが、環境構築の方法と、typeorm の設定方法、ormconfig.jsonormconfig.tsにする方法は以下の記事を参考にしてください。
https://zenn.dev/msksgm/articles/20211107-typeorm-ormconfig

テーブル定義

以下はUserエンティティとWorkエンティティです。
多対多ですので、中間テーブルが必要です。

Userエンティティ。

src/entity/User.ts
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  firstName: string;

  @Column()
  lastName: string;

  @Column()
  age: number;
}

Workエンティティ。

src/entity/Work.ts
import { Column, Entity, PrimaryGeneratedColumn, Unique } from "typeorm";

@Entity()
@Unique(["title"])
export class Work {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column()
  body: string;
}

以下は、中間テーブルのUserWorkエンティティです。
typorm の中間テーブルを定義しています。

UserWorkエンティティ。

src/entity/UserWork.ts
import { Entity, ManyToOne, JoinColumn, PrimaryColumn } from "typeorm";
import { User } from "./User";
import { Work } from "./Work";

@Entity()
export class UserWork {
  @PrimaryColumn()
  user_id: number;

  @PrimaryColumn()
  work_id: number;

  @ManyToOne(() => User, (user) => user.id, {
    onDelete: "CASCADE",
  })
  @JoinColumn({ name: "user_id" })
  public user!: User;

  @ManyToOne(() => Work, (work) => work.id, {
    onDelete: "CASCADE",
  })
  @JoinColumn({ name: "work_id" })
  public work!: Work;
}

動作確認

./src/index.tsに DB への接続とデータの挿入を定義しています。

./src/index.ts
import "reflect-metadata";
import { createConnection } from "typeorm";
import { User } from "./entity/User";
import { Work } from "./entity/Work";
import { UserWork } from "./entity/UserWork";

createConnection()
  .then(async (connection) => {
    console.log("Inserting a new user into the database...");
    const user1 = new User();
    user1.firstName = "hoge";
    user1.lastName = "hoge";
    user1.age = 25;
    await connection.manager.save(user1);

    const user2 = new User();
    user2.firstName = "fuga";
    user2.lastName = "fuga";
    user2.age = 25;
    await connection.manager.save(user2);

    const user3 = new User();
    user3.firstName = "piyo";
    user3.lastName = "piyo";
    user3.age = 35;
    await connection.manager.save(user3);

    console.log("Inserting a new work into the database...");
    const work1 = new Work();
    work1.title = "フロント";
    work1.body = "運用";
    await connection.manager.save(work1);

    const work2 = new Work();
    work2.title = "バックエンド";
    work2.body = "API作成";
    await connection.manager.save(work2);

    const work3 = new Work();
    work3.title = "DB";
    work3.body = "ER設計";
    await connection.manager.save(work3);

    console.log("Inserting a new userWork into the database...");
    const userWork1 = new UserWork();
    userWork1.user_id = 1;
    userWork1.work_id = 1;
    await connection.manager.save(userWork1);

    const userWork2 = new UserWork();
    userWork2.user_id = 1;
    userWork2.work_id = 2;
    await connection.manager.save(userWork2);

    const userWork3 = new UserWork();
    userWork3.user_id = 2;
    userWork3.work_id = 1;
    await connection.manager.save(userWork3);

    const userWork4 = new UserWork();
    userWork4.user_id = 3;
    userWork4.work_id = 3;
    await connection.manager.save(userWork4);

    console.log("Here you can setup and run express/koa/any other framework.");
  })
  .catch((error) => console.log(error));

ts-node ./src/index.tsで実行すると、データの挿入が完了します。

ts-node ./src/index.ts

DB を確認すると、中間テーブルが生成されたため、リレーションがとれていることがわかります。

mysql -u root --port 3306 -h 127.0.0.1 -D typeorm_db -e 'SELECT * FROM user; SELECT * FROM work; SELECT * FROM user_works;' -p
+----+-----------+----------+-----+
| id | firstName | lastName | age |
+----+-----------+----------+-----+
|  1 | hoge      | hoge     |  25 |
|  2 | fuga      | fuga     |  25 |
|  3 | piyo      | piyo     |  35 |
+----+-----------+----------+-----+
+----+--------------------+-----------+
| id | title              | body      |
+----+--------------------+-----------+
|  1 | フロント           | 運用      |
|  2 | バックエンド       | API作成   |
|  3 | DB                 | ER設計    |
+----+--------------------+-----------+
+---------+---------+
| user_id | work_id |
+---------+---------+
|       1 |       1 |
|       2 |       1 |
|       1 |       2 |
|       3 |       3 |
+---------+---------+

参考

https://github.com/typeorm/typeorm/issues/4653

Discussion