LoginSignup
7

More than 3 years have passed since last update.

TypeORM - ManyToManyリレーションで中間テーブルにカスタムな名前付けする方法

Posted at

はじめに

TypeORMのマイグレーション機能使えば、中間テーブル名は勝手に名付けてくれるのですが、entityA_columnA_entityBという名前になり、ミーニングレス感が否めないテーブル名になります
またDatabaseを他プロジェクトと共有していて、マイグレーションをTypeORMでやってないパターンのとき困ったりしました(あとテーブル名に規則があったりしますよねー)
今回は、JoinTableデコレータを使って、カスタムな名前付けをしたテーブル・カラム名でも問題なく、マイグレーション・データ取得できる方法をメモします

結論

@Entity({
  name: 'entity_a',
})
export class EntityA {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToMany(type => EntityB)
  @JoinTable({
    name: 'junction',
    joinColumn: {
      name: 'entity_a_id',
      referencedColumnName: 'id',
    },
    inverseJoinColumn: {
      name: 'entity_b_id',
      referencedColumnName: 'id',
    },
  })
  entityBs: EntityB[];
}

@Entity({
  name: 'entity_b',
})
export class EntityB {
  @PrimaryGeneratedColumn()
  id: number;
}

Step by stepで見ていく

EntityA,Bでは分かりづらいので、UserエンティティとSkillエンティティ、その多対多リレーションを実現する中間テーブルを考えます

まずTypeORMマイグレーションでやってみると

UserエンティティとSkillエンティティを準備します

@Entity({
  name: 'users',
})
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 32 })
  name: string;

  @Column({ length: 32 })
  email: string;

  @ManyToMany(type => Skill)
  @JoinTable()
  skills: Skill[];
}

@Entity({
  name: 'skills',
})
export class Skill {
  @PrimaryGeneratedColumn()
  code: number;

  @Column({ length: 32 })
  name: string;
}

これをマイグレーションするとこんなテーブルができました


mysql> show tables;
+---------------------+
| Tables_in_database  |
+---------------------+
| migrations          |
| skills              |
| users               |
| users_skills_skills |
+---------------------+

確認のためこんなコードを書きました

import "reflect-metadata";
import { createConnection } from "typeorm";
import { Skill } from "./skill.master";
import { User } from "./user.entity";

// npx ts-node ./src/entities/test.ts 

createConnection().then(async connection => {
  const skill = new Skill();
  skill.name = 'TypeORM';

  await connection.manager.save(skill);

  const user = new User();
  user.name = 'karino';
  user.email = 'email';
  user.skills = [skill];

  await connection.manager.save(user);

  const result = await connection.createQueryBuilder()
    .select('users')
    .from(User, 'users')
    .leftJoinAndSelect('users.skills', 'skill')
    .orderBy('id', 'DESC')
    .getOne();

  console.log(result);
}).catch(error => console.log(error));

// User {
//  id: 7,
//  name: 'karino',
//  email: 'email',
//  skills: [ Skill { code: 9, name: 'TypeORM' } ]
// } 

ではTypeORMでマイグレーションしたものをJoinTableのプロパティで再現する


  @ManyToMany(type => Skill)
  @JoinTable()
  skills: Skill[];

  ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

  @ManyToMany(type => Skill)
  @JoinTable({
    name: 'users_skills_skills',
    joinColumn: {
      name: 'usersId',
      referencedColumnName: 'id',
    },
    inverseJoinColumn: {
      name: 'skillsCode',
      referencedColumnName: 'code',
    },
  })
  skills: Skill[];
  • JoinTablenameに設定したいテーブル名(users_skills_skills)を当てます
  • joinColumnには、このエンティティ側の設定を記述します
    • nameには中間テーブル側のカラム名
    • referencedColumnNameにはエンティティ側のカラム名
  • inverseJoinColumnには関連付けたいエンティティ側の設定を記述します
    • name, referencedColumnName共に同じ要領で設定

この状態でマイグレーションしても、まったく同じマイグレーションファイルが生成されることを確認しました
確認用のテストコードも変更なく問題なく動作しました

最後にカスタムなテーブル名を付けてみる

  • 中間テーブル名は、user_skills
  • user_id, skill_codeのカラムを持つ

ようにしたいです。やってみましょう

  @JoinTable({
-   name: 'users_skills_skills',
+   name: 'users_skills',
    joinColumn: {
-     name: 'usersId',
+     name: 'user_id',
      referencedColumnName: 'id',
    },
    inverseJoinColumn: {
-     name: 'skillsCode',
+     name: 'skill_code',
      referencedColumnName: 'code',
    },
  })

分かってみるとシンプルですが、最初はややこしかったです。ORMはActiveRecordしか知らないので相場がわかりません
確認用のテストコードも変更なく問題なく動作しましたが、マイグレーションしてみると新しくusers_skillsテーブルがCreateされて、users_skills_skillsに変更はありませんでした
まあ手動でマイグレーションファイルを書き換えればいいレベルだと思います

余談:中間テーブルにもcteatedAtとかカスタムなプロパティを入れたいんだけど…

TypeORMの機能としては、エンティティを作らない方法では、プロパティを追加することは出来ないようです
新しくエンティティを定義してあげる必要があります

References

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
7