Closed11
TypeORMの知見まとめ
TypeORMのリレーションでSoft Deleteが機能してない問題
以下のissuesを参照すると、同じ問題が発生している人が多数いるみたい👀
現状は、対応して無さそう...
orphanedRowActionオプションのメモ
以下のissuesなどを参照
orphanedRowAction
: "nullify" | "delete" - 子行が親から削除された場合、その子行をそのままにするか(デフォルト)、削除するかを決定します。
cascadeを有効にしている場合はinsertは使わない方が良いかも?
cascadeとは、リレーションを設定する時に渡させるオプションの事。
詳しくはドキュメントを参照して欲しいが、例えば以下のような設定。
cascadeオプションを渡す例
@Entity()
class Hoge {
/* ... */
@OneToOne(() => Item, { cascade: true })
item: Item;
/* ... */
}
そして、cascadeを設定した上記のようなエンティティに以下のような処理をした場合、insert()
と save()
で挙動が変わる。
const repo = getRepository(Hoge);
const item = new Item()
const hoge = repo.create({ /* ... */ })
item.name = 'Hello World!'
hoge.item = item
await repo.save(profile) // itemも一緒に保存される ( 想定していた挙動 )
await repo.insert(profile) // hogeは保存されるが、itemは保存されない( 想定外の挙動 )
なるべくは、save()
を使った方が良さそう。
紐づいているレコード(行)も論理削除する方法
先ず初めに、リレーションを定義する際に cascade
オプションを有効にする必要がある。
User.ts
@Entity()
export class User { /* ... */ }
Profile.ts
@Entity()
export class Profile {
/* ... */
// cascade: true を設定する必要がある!
@OneToOne(() => User, { cascade: true })
@JoinColumn()
user: User
/* ... */
}
次に、削除したいレコード(行)を取ってくるときに relations
を指定して取得し、その返り値を softRemove
に渡すことで、紐づいているレコード(行)も論理削除が出来る。
const repo = getRepository(Profile)
const profile = repo.findOne({ id: 1, relations: ["user"] }) // relationsを指定する必要がある!
// Profileのカラムを削除
// 紐づいているUserのレコードも削除される
repo.softRemove(profile)
// softDeleteだと削除できないので注意!
// repo.softDelete( profile )
@OneToMany()で、紐づいているレコードだけ削除・論理削除する方法
物理削除の場合
エンティティは以下のように定義する。
User.ts
@Entity()
export class User {
/* ... */
// 特に設定は無し
@OneToMany(() => Task, (task) => task.user)
tasks: Task[]
/* ... */
}
Task.ts
@Entity()
export class Task {
/* ... */
// orphanedRowAction: 'delete' を設定する
// これによって、Userとの紐づけが切れた時に自動的に物理削除される
// orphanedRowAction: 'nullify' を設定した場合は、
// プライマリーカラム( 今回は 'userId' )に null が入る( デフォルトはコレ )
@ManyToOne(() => User, { orphanedRowAction: 'delete' })
user: User
/* ... */
}
その次に以下のようにする事で、Task
のみのレコードを 物理削除 することが出来ます。
Taskのレコードのみ削除する
const userRepo = getRepository(User)
const taskRepo = getRepository(Task)
// relationsを設定しないと、tasksがロードされないので注意!
const user = await userRepo.findOne({ relations: ['tasks'] })
console.log( user.tasks ) // [{ ... }, { ... }] <- タスクが複数登録されている事を確認
user.tasks = [] // 登録されているタスクを全て削除
// 指定して削除したい場合は、削除したい値を配列から除けばよい
// user.tasks = user.tasks.filter(v => v.id !== deleteTaskId)
await userRepo.save(user) // 配列に入っていないTaskを削除
論理削除する場合
論理削除の場合、エンティティ内での設定は特にない。
というより、対応していない。強いて言えば、@DeleteDateColumn()
を設定するくらい。
User.ts
@Entity()
export class User {
/* ... */
// 特になし
@OneToMany(() => Task, (task) => task.user)
tasks: Task[]
/* ... */
}
Task.ts
@Entity()
export class Task {
/* ... */
// 特になし
@ManyToOne(() => User)
user: User
// 論理削除用のカラム
@DeleteDateColumn()
deletedAt?: Date
/* ... */
}
そのため、論理削除するには強引に softRemove()
を実行する必要がある。
Taskのレコードのみを論理削除する
const userRepo = getRepository(User)
const taskRepo = getRepository(Task)
// relationsを設定しないと、tasksがロードされないので注意!
const user = await userRepo.findOne({ relations: ['tasks'] })
console.log( user.tasks ) // [{ ... }, { ... }] <- タスクが複数登録されている事を確認
await taskRepo.softRemove( user.tasks ) // 登録されているタスクを全て削除
// 指定して削除したい場合は、削除したい値を配列に含める
// await taskRepo.softRemove( user.tasks.filter(v => v.id === deleteTaskId) )
TypeORMでCircularRelationsErrorが発生した時
上記のようなエラーが発生した場合は、どこかのリレーションで nullable : false
としている所を true
に設定するか、設定しない必要がある。
example.ts
@Entity()
class User {
- @OneToMany(() => Tag, (tag) => Tag.user, { cascade: true, nullable: fasle })
+ @OneToMany(() => Tag, (tag) => Tag.user, { cascade: true })
tags: Tag[];
}
このスクラップは2021/05/07にクローズされました