관계(Relation)
관계(Relation)
관계형 데이터베이스는 데이터 중복을 방지하기 위해 테이블간 관계를 사용합니다.
테이블 간 관계에는 네 종류가 있습니다.
- 일대일 관계(1:1, One to One)
- 일대다 관계(1:N, One to Many)
- 다대일 관계(N:1, Many to One)
- 다대다 관계(N:M, Many to Many)
PL/SQL이나 MyBatis를 사용하던 사람들에게 주의사항
데이터베이스 테이블은 부모 자식 관계가 없습니다.
하지만 ORM에는 부모 자식 관계가 있다고 생각하세요.
관계 옵션
옵션 | 설명 |
eager | true 설정 시 find(), findOne(), QueryBuilder 사용 시 참조하는 엔티티를 항상 조회합니다. 기본값 fasle. |
cascade | true 설정 시 insert, update 쿼리 시 참조하는 엔티티의 행이 함께 insert 혹은 update 됩니다. 기본값 false. true, false 대신 배열에 쿼리 종류를 지정할 수도 있습니다. [ 'insert', 'update', 'remove', 'soft-remove', 'recover' ] |
onDelete | 부모 엔티티의 행이 삭제될 때 외래키가 어떻게 동작해야하는지 지정합니다. onDelete 속성은 자식 엔티티에서 사용해야 합니다. ‘NO ACTION’: 아무것도 안함. 기본값 ‘CASCADE’: 참조하는 row도 같이 삭제 ‘SET NULL’: null로 변경 ‘DEFAULT’: 테이블의 기본 설정 값으로 변경 ‘RESTRICT’: 참조하는 row가 있는 경우 삭제 불가 |
nullable | 관계키가 null 허용인지 여부를 지정합니다. true: 기본값 false: 관계 컬럼에 값이 없으면 저장 불가능 |
orphanedRowAction | 부모가 삭제되는 경우 자식의 동작을 정의합니다. (고아 객체라고도 하네요.) 기본값 disable. delete: 자식 삭제(update의 경우인 듯? insert는 불가능하니까) soft-delete: 자식 soft-delete nullify: 관계키만 삭제 disable: 관계는 유지. 삭제하려면 자식의 저장소(repository)를 사용해야 합니다. |
일대일 관계
일대일 관계는 두 가지 종류가 있습니다.
- 단방향 일대일 관계
- 양방향 일대일 관계
자식 Profile를 부모 User 엔티티가 참조하는 예시를 단방향, 양방향으로 각각 만들어보겠습니다.
단방향 일대일 관계
두 엔티티 중 하나의 엔티티에만 관계 데코레이터를 선언하면 단방향 일대일 관계가 됩니다.
부모 엔티티에 @OneToOne(), @JoinColumn() 데코레이터를 사용합니다.
이때는 자식 엔티티의 PK를 부모 엔티티의 외래키로 저장합니다.
profile.entity.ts
import { Entity, PrimaryGeneratedColumn, Coloumn } from 'typeorm'
@Entity()
export class Profile {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}
user.entity.ts
import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToOne,
JoinColumn,
} from "typeorm"
import { Profile } from "./Profile"
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
username: string
@OneToOne(() => Profile,{
casecade: true,
eager: true,
})
profile: Profile
}
단방향 관계에서는 casecade 옵션에 'remove'를 넣거나, onDelete 옵션에 'CASCADE'를 넣어도 자식 엔티티의 데이터가 삭제되지 않습니다.
양방향 일대일 관계
두 엔티티 모두에 @OneToOne() 데코레이터를 사용해야 합니다.
자식 엔티티에 @JoinColumn() 데코레이터를 사용합니다.
이때는 부모 엔티티의 PK를 자식 엔티티의 외래키로 저장합니다.
onDelete 옵션을 자식 엔티티에서 사용하면 부모 엔티티의 행 삭제 시 자식 엔티티의 행도 삭제됩니다.
profile.entity.ts
import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToOne,
JoinColumn,
} from 'typeorm';
import { User } from './user.entity';
@Entity()
export class Profile {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToOne(() => User, (user) => user.id, {
onDelete: 'CASCADE',
})
@JoinColumn()
user: string;
}
user.entity.ts
import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToOne,
} from "typeorm"
import { Profile } from "./Profile"
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
username: string
@OneToOne(() => Profile, (profile) => profile.user, {
// casecade와 eager 옵션은 권장되지 않지만 예시로 작성했습니다.
casecade: true,
eager: true,
})
profile: Profile
}
일대다, 다대일 관계
부모 엔티티에 @OneToMany() 데코레이터를, 자식 엔티티에 @ManyToOne() 데코레이터를 사용합니다.
@JoinColumn() 데코레이터는 사용하지 않습니다.
부모 엔티티의 PK를 자식 엔티티의 외래키로 저장합니다.
photo.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"
import { User } from "./User"
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number
@Column()
url: string
@ManyToOne(() => User, (user) => user.photos)
user: User
}
user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"
import { Photo } from "./Photo"
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@OneToMany(() => Photo, (photo) => photo.user)
photos: Photo[]
}
관계를 사용한 조회
조회 시 relation 사용
w
eager 옵션 사용
관계를 사용한 저장(insert, update)
저장 시 컬럼 값 지정
cascade 옵션