TypeORM을 사용한 엔티티 관계 정의(ManyToMany)

hyejin·2024년 6월 27일
0

study-2024

목록 보기
8/16

N:M 관계

다대다(N:M) 관계는 데이터베이스 설계에서 두 엔티티가 서로 다수의 인스턴스와 관계를 가질 수 있는 경우를 의미한다. 예를 들어, 학생과 수업 사이의 관계를 생각해볼 수 있다. 한 학생은 여러 수업을 들을 수 있고, 한 수업도 여러 학생이 들을 수 있다.

ManyToMany 관계 정의

ManyToMany 관계에는 두 엔티티를 연결하는 중간테이블이 생성되며, @JoinTable()을 사용하여 중간테이블 명과 참조 컬럼을 명시해줄 수 있다.

@JoinTable()만 사용하는 경우

// import 생략

@Entity()
export class Student {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @ManyToMany(() => Course, (course) => course.students)
  @JoinTable()
  courses: Course[];
}

@Entity()
export class Course {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @ManyToMany(() => Student, (student) => student.courses)
    students: Student[];
}

@JoinTable()은 관계의 한쪽(소유)에 넣어야 하며, student_courses_course 라는 중간 테이블이 생성되는 것을 확인할 수 있다. 또한, 참조 컬럼은 각 엔티티의 기본키이다.

@JoinTable()에 테이블명과 참조 컬럼을 명시한 경우

// import 생략

@Entity()
export class Student {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @ManyToMany(() => Course, (course) => course.students)
    @JoinTable({
        name: 'student_course_relation',
        joinColumn: { name: 'studentId', referencedColumnName: 'id' },
        inverseJoinColumn: { name: 'courseId', referencedColumnName: 'id' },
    })
    courses: Course[];
}

@Entity()
export class Course {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @ManyToMany(() => Student, (student) => student.courses)
    students: Student[];
}

name 옵션으로 중간 테이블 명을 지정하고, 참조할 컬럼과 명은 joinColumn과 inverseJoinColumn을 사용해 지정해주면 된다.
위의 경우는 student_course_relation 이라는 명으로 중간 테이블이 생성되고, 현재 엔티티의 id 컬럼을 참조한 외래 키의 명이 studentId이다. 또, 상대 엔티티인 Course 엔티티의 id 컬럼을 참조한 외래 키의 명이 courseId 이다.

@JoinTable() 기본 옵션

  • name: 중간 테이블의 이름 지정(지정하지 않으면 기본적으로 엔티티 이름을 조합하여 생성)
  • joinColumn: 현재 엔티티에서 중간 테이블로 가는 방향의 외래 키 지정
    • name: 외래 키 컬럼의 이름 지정
    • referencedColumnName: 현재 엔티티의 컬럼을 참조
  • inverseJoinColumn: 상대 엔티티에서 중간 테이블로 가는 방향의 외래 키 지정
    • name: 외래 키 컬럼의 이름 지정
    • referencedColumnName: 상대 엔티티의 컬럼을 참조

옵션 지정

// import 생략

@Entity()
export class Student {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @ManyToMany(() => Course, (course) => course.students, {
    	cascade: true,
      	eager: true,
      	onDelete: 'CASCADE',
      	onUpdate: 'CASCADE',
    })
    @JoinTable({
        name: 'student_course_relation',
        joinColumn: { name: 'studentId', referencedColumnName: 'id' },
        inverseJoinColumn: { name: 'courseId', referencedColumnName: 'id' },
    })
    courses: Course[];
}

@Entity()
export class Course {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @ManyToMany(() => Student, (student) => student.courses)
    students: Student[];
}

Student와 Course 두 군데에 casecade와 eager를 작성하면 에러 발생한다.

  • cascade: true
    부모 엔티티에 수행되는 작업을 자식 엔티티에도 자동으로 적용하는 역할을 한다. 부모 엔티티에서 실행되는 생성, 수정, 삭제 등의 작업이 자식엔티티에도 영향이 간다.
  • eager: true
    엔티티를 로드할 때 관련된 엔티티를 자동으로 함께 로드하는 역할을 한다. 위 코드에선 Student 데이터를 조회할때 연결된 Course 데이터가 함께 조회되고, Course 데이터를 조회할때는 Course 데이터만 조회가 된다.
  • onDelete: 'CASCADE'
    onDelete 옵션은 참조된 외래 키의 레코드가 삭제될 때, 해당 외래 키를 어떻게 처리할지를 정의하는 역할을 하며, Student가 삭제될 때 student_course_relation에 해당 레코드도 함께 삭제된다.
  • onUpdate: 'CASCADE'
    onUpdate 옵션은 참조된 외래 키가 업데이트될 때, 해당 외래 키를 어떻게 처리할지를 정의하는 역할을 한다. 만약 Student의 id가 수정되면 student_course_relation에도 적용이 된다.

만약 Course 엔티티를 삭제하거나 수정한 경우에 중간테이블의 외래 키 처리 방식을 정의하고싶다면?

@Entity()
export class Course {
  	...
    @ManyToMany(() => Student, (student) => student.courses, {
        onUpdate: 'CASCADE',
        onDelete: 'CASCADE',
    })
    students: Student[];
}

위와 같이 작성해주면 Course 엔티티가 삭제되거나 업데이트 된 경우, 중간 테이블의 데이터에 영향을 미치게된다.



[참고자료]

https://00h0.tistory.com/19

profile
노는게 제일 좋아

0개의 댓글