TypeORM을 사용한 엔티티 관계 정의(OneToMany, ManyToOne)

hyejin·2024년 6월 7일
0

study-2024

목록 보기
7/17
post-custom-banner

TypeORM을 사용한 엔티티 관계 정의 중 ManyToOne, OneToMany에 관해 알아보자.

1:N 관계

일대다(1:N) 관계는 한 쪽 엔티티가 관계를 맺은 엔티티 쪽의 여러 객체를 가질 수 있는 것을 의미한다. 즉, 테이블A 한 개의 행이 테이블B 여러 개의 행과 연결되는 관계를 말한다. 이해하기 쉽게 예를 들자면, 국가와 도시가 있다. 하나의 국가(1)은 여러 개의 도시(N)를 가질 수 있다.

ManyToOne, OneToMany 관계 정의

@ManyToOne(), @OneToMany() 관계에서 @ManyToOne() 데코레이터가 작성되는 엔티티가 자식엔티티이다. 1:N 관계에서는 일반적으로 자식 엔티티에 @JoinColumn()를 작성하여 외래키를 연결한다.
NestJS에서 TypeORM을 이용하여 entity 관계를 설정하는 방법은 아래와 같다.

// import 생략

@Entity()
export class Country {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    name: string

    @OneToMany(() => City, (city) => city.country, {
    	cascade: true,
    	eager: true 
  	})
    cities: City[]
}

@Entity()
export class City {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    name: string

    @ManyToOne(() => Country, (country) => country.cities, {
    	onDelete: 'CASCADE',
    	onUpdate: 'CASCADE',
  	})
    @JoinColumn({name: 'country_id', referencedColumnName: 'id'})
    country: Country
}
  • eager
    • 관련된 엔티티를 즉시 로드하는 기능.
    • 부모 엔티티를 조회할 때, TypeORM의 find() 또는 findOne() 메서드를 사용하여 relations 옵션 없이 바로 연결된 자식 엔티티도 함께 로드할 수 있다.
    • createQueryBuilder()에서는 적용되지 않는다.
  • cascade
    • 부모 엔티티의 변경사항이 자식 엔티티에 자동으로 전파되는 기능.
    • 해당 옵션을 통해 부모 엔티티의 생성, 업데이트, 삭제 작업이 자식 엔티티에도 자동으로 적용될 수 있다.
    • save() 메서드를 사용할 때, 부모 엔티티를 생성함과 동시에 자식엔티티도 함께 생성할 수 있지만, createQueryBuilder()에서는 이 옵션이 적용되지 않는다.
    • 부모 엔티티의 변경 사항이 자식 엔티티의 외래 키에 어떤 영향을 미칠지는 onDeleteonUpdate 옵션을 사용하여 정의한다. 이 옵션들은 TypeORM에서 ManyToOne과 OneToMany 관계를 설정할 때 외래 키의 동작을 제어하는 데 사용된다.
  • onDelete
    • 외래 키가 참조하는 레코드가 삭제될 때, 외래 키의 처리 방식을 정의하는 옵션.
    • 다른 테이블의 레코드와의 일관성을 유지하기 위해 사용된다.
    • 일반적으로 사용되는 옵션에는 CASCADE(자동 삭제), RESTRICT(삭제 거부), SET NULL(외래키 NULL로 설정), NO ACTION(아무 작업 X), SET DEFAULT(기본값) 등이 있다.
  • onUpdate
    • 외래 키가 참조하는 키 값이 업데이트될 때, 외래 키의 처리 방식을 정의하는 옵션.
    • 외래 키가 참조하는 테이블의 레코드가 업데이트될 때 필요한 조치를 지정한다.
    • CASCADE(자동 업데이트), RESTRICT(참조된 테이블의 레코드가 없을때만 업데이트 가능), SET NULL(외래키 NULL로 설정), NO ACTION(아무 작업 X)과 같은 옵션을 설정할 수 있다.

@JoinColumn()을 작성하면 상대 엔티티의 기본키를 참조하며, 참조할 컬럼을 직접 지정(referencedName)할 수 있고, 외래키 명(name)도 지정할 수 있다.
단, 참조할 컬럼은 기본키이거나 unique한 값이어야 한다.
아래 코드는 City 엔티티가 Country 엔티티의 name 컬럼을 외래키로 참조하는 예제이다.

// import 생략

@Entity()
export class Country {
    @PrimaryGeneratedColumn()
    id: number

    @Column({ unique: true })
    name: string

    @OneToMany(() => City, (city) => city.country, {
    	cascade: true,
    	eager: true 
  	})
    cities: City[]
}

@Entity()
export class City {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    name: string

    @ManyToOne(() => Country, (country) => country.cities, {
    	onDelete: 'CASCADE',
    	onUpdate: 'CASCADE',
  	})
    @JoinColumn({name: 'country_name', referencedColumnName: 'name'})
    country: Country
}

@JoinColumn()을 생략할 수도 있는데, 생략하면 TypeORM은 자동으로 외래키를 생성하고 관련된 컬럼을 참조하게 된다.(Country 엔티티의 기본키인 id가 참조됨)
또한, ManyToOne()의 경우 관계 정의가 필수적이지만, OneToMany()의 경우에는 생략이 가능하다.

// import 생략

@Entity()
export class Country {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    name: string
}

@Entity()
export class City {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    name: string

    @ManyToOne(() => Country, (country) => country.cities, {
    	onDelete: 'CASCADE',
    	onUpdate: 'CASCADE',
  	})
    country: Country
}

[참고자료]

https://velog.io/@gagaeun/%EA%B4%80%EA%B3%84%ED%98%95-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-11-1N-NM-%EA%B4%80%EA%B3%84

profile
노는게 제일 좋아
post-custom-banner

0개의 댓글