はじめに
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[];
-
JoinTable
のname
に設定したいテーブル名(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
とかカスタムなプロパティを入れたいんだけど…
- Adding extra columns to a join table/relationship table
- typeorm - how to add extra field to “many to many” table?
TypeORMの機能としては、エンティティを作らない方法では、プロパティを追加することは出来ないようです
新しくエンティティを定義してあげる必要があります