Open1

TypeORMのsaveで子供もinsertするには

こばちきこばちき

概要

TypeORMのsaveメソッド(リポジトリ、エンティティマネージャ共)に親子関係(OneToOne、OneToMany)のエンティティで子供側もinsertさせる方法

ParentエンティティとChildエンティティがあり、それらの関係が1対多の関係にあります。

Parent "1" -- "0..*" Child

Parent.ts

@Entity()
class Parent {
  @PrimaryColumn()
  id!: string;

  @Column()
  code!: string

  @Column()
  name!: string

  @VersionColumn()
  version?: number;

  @OneToMany(() => Child, (child) => child.parent)
  children!: Child[];
}

Child.ts

@Entity()
class Child {
  @PrimaryColumn()
  id!: string;

  @Column()
  code!: string

  @Column()
  name!: string

  @VersionColumn()
  version?: number;

  @ManyToOne(() => Parent, (parent) => parent.children)
  parent!: Parent;
}

ParentService.ts

@Injectable()
export class ParentService {
  constructor(@InjectRepository(Parent) private readonly parentRepository: Repository<Parent>) {}

  async save(paret: Parent): Promise<void> {
    this.parentRepository.save(parent);
  }
}

まだ未登録のparent.id = 1とchild.id = 1とchild.id = 2をデータベースに登録するために、以下のように記述します。

const parent = new Parent();
parent.id = "1";
parent.code = "P01";
parent.name = "親";
parent.children = [];

const child1 = new Child();
child1.id = "1";
child1.code = "C0001";
child1.name = "子供1";
children.push(child1);

const child2 = new Child();
child2.id = "2";
child2.code = "C0002";
child2.name = "子供2";
children.push(child2);

parentService.save(parent);

parentService.save(parent)で、parentのinsertが実行されるのは想定通りですが、childはupdateが実行されます。

UPDATE child SET code = 'C0001', name = '子供1', version = version + 1 WHERE id = '1' AND version = 1;
UPDATE child SET code = 'C0002', name = '子供2', version = version + 1 WHERE id = '2' AND version = 1;

childもinsertを実行させるためには、@OneToManyの属性としてcascade: ['insert']を追加します。

  @OneToMany(() => Child, (child) => child.parent, { cascade: ['insert'] })
  children!: Child[];

parentService.save(parent)を実行すると、Childのidと一致するレコードが存在するか確認して、存在しなければinsertを、存在すればupdateを実行するようになります。