GAミント至上主義

Web Monomaniacal Developer.

TypeORMのcascade, orphanedRowAction等RelationOptionsの違い

TypeORMのEntityで使うRelation系のオプションがいろいろ紛らわしいのでメモ。

バージョン

"typeorm": "^0.2.37"

DBはMySQLを使用。

公式ドキュメントはここらへん

typeorm/relations.md at master · typeorm/typeorm · GitHub

Entityはこんな感じのサンプル通りのやつ

export class User {
  @PrimaryGeneratedColumn()
  id: number

  @OneToOne(() => UserProfile, (profile) => profile.user, {
    cascade: true,
    orphanedRowAction: 'delete',
    onDelete: 'CASCADE',
  })
  @JoinColumn()
  profile: UserProfile

  // 省略
}

cascade

名前的にDB側のテーブル設定かと思いきや違うので、設定を変更してもmigration は発生しない。 親Entityを各種更新したときにいっしょに子も更新するかどうか。 取得時には影響しない

選択肢はboolまたは "insert" | "update" | "remove" | "soft-remove" | "recover"

onDelete, OnUpdate

これがDB構造に影響するもの。 下記のようにほぼそのまんまALTER等される。変更時はmigration必要。 将来アプリ側で制御できなくて困る可能性もあるので慎重にした方がよさそう。

 await queryRunner.query(`ALTER TABLE \`search_db\`.\`user\` ADD CONSTRAINT \`FK_9466682df91534dd95e4dbaa616\` FOREIGN KEY (\`profileId\`) REFERENCES \`search_db\`.\`user_profile\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`);

eagar

取得時にそのEntityも一緒に取得するかどうか。DB構造には影響しない。

orphanedRowAction

ちょっとややこしいのがこれ。orphanは孤児的な意味。DB構造には影響しない。 親Entityで子Entityを消してsaveした場合、いっしょに消すかどうか。

デフォルトでは子に入っている親IDをnullにしようとする(当然nullable: falseの場合はエラー)。

選択肢は"nullify" | "delete"

追加時のプルリクが参考になる。

feat: relations: Orphaned row action by nebkat · Pull Request #7105 · typeorm/typeorm · GitHub

This adds an additional option on one-to-many relationships where child entities are deleted, rather than having their foreign key set to null, when removed from their parent.

上記の通り基本的にはOneToManyのMany側に使用する。 設定しないとOne側が削除された場合、Many側はOneのIDをnullにする動作をするが、deleteにすれば行ごと削除してくれる。 基本的に関連キーはnullable: falseにすることが多いので、これ付けないとOne側削除時にエラーになる場面が大半な気がする。

OneToOneにも設定できてしまうけど意味はなさそう。

テストではCategoryにPostを2つ追加したsaveした後、1つ消して再度save, Postも一緒に消えてるかどうかを検証してる。

feat: relations: Orphaned row action by nebkat · Pull Request #7105 · typeorm/typeorm · GitHub

さいごに

ここらへん、設計時にはなるべく連動して消えたり保存されたりしてほしいと思うものの、実際はいろんな問題起きたりするので、どれを設定するかしないかが結構悩みどころ。 OneToOne でも設定はできるけど、意図した動きしないのでちょっと検証中。