TypeORM - Entity 작성하기 (modeling)

Posted by Juri on November 15, 2021

Entity

Entity is your model decorated by @Entity decorator.

Entity는 데이터베이스의 테이블과 맵핑되는 클래스이다. @Entity를 사용해서 새로운 클래스를 정의하여 Entity를 생성할 수 있다.

Entity 작성하기

1
2
3
4
5
6
7
import {
    Entity,
    PrimaryGeneratedColumn,
    Column,
    ManyToMany,
    JoinColumn
} from "typeorm"

필요한 데코레이터들을 typeorm에서 import 해온다. 불러온 데코레이터를 이용해 간단한 모델링을 해보도록 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Entity()
export class Actor {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @Column({ type: "varchar", name: "movie_title", length: 200 })
    movie: string;
}

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

    @Column({ type: "varchar", name: "movie_title", length: 200 })
    title: string;

    @Column({ type: "nvarchar", name: "description", length: 1000, nullable: true })
    description: string;

    @Column()
    releasedDate: Date;

    @Column({ default: false })
    isSeries: boolean;

    @CreateDateColumn()
    createdAt: Date;

    @ManyToMany(() => Actor { onDelete: 'CASCADE' })
    @JoinTable()
    actors: Actor[]
}
  • 컬럼에 속성 부여하기 @Column()의 안에 들어갈 수 있는 옵션은 type, name, length, default, nullable 등 다양하다. 컬럼에 들어올 값의 타입, 이름, 길이, 디폴트값, 널값 여부등을 지정한다.

  • nvarchar nvarchar : 유니코드를 지원하는 데이터형으로 다국어지원이 필요한 데이터일 때는 varchar가 아닌 nvarchar로 설계한다.

  • PK키 설정하기 @PrimaryGeneratedColumn()을 사용한다.

  • CreateDate와 UpdateDate 설정하기 @CreateDateColumn()@UpdateDateColumn() 를 사용해 쉽게 설정할 수 있다.

    type 설정하기

    int, tinyint, bigint, bool, datetime, varchar 등

enum type

컬럼에 들어올 값을 미리 정해놓을 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export type Extension = "jpeg" | "png"

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

    @Column({
        type: "enum",
        enum: ["jpeg", "png"]
        default: "png"
    })
    extension: Extension
}

generated values

컬럼값으로 랜덤한 문자열(uuid)을 생성할 수 있다.

1
2
3
4
5
6
7
8
9
@Entity()
export class User {
    @primaryGeneratedColumnmn()
    id: number;

    @Column()
    @Generated("uuid")
    userId: string;
}

Entity 상속 ‘확장(extends)’을 사용하면 다른 Entity를 상속받아 Entity를 생성할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
export abstract class Content {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    title: string;
}

@Entity()
export class Photo extens Content {
    @Column()
    extension: Extension
}

참고 : Typescript - interface 확장하기

relations 설정하기

1. one-to-one relations

참고: typeorm 공식 홈페이지

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import {
    Entity,
    PrimaryGeneratedColumn,
    Column,
    OneToOne,
    JoinColumn
} from "typeorm";       
import { Profile } from "./Profile";

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

    @Column()
    name: string;
    
    @OneToOne(() => Profile)
    @JoinColumn()
    profile: Profile;
}

@JoinColumn()을 가진 쪽의 테이블이 Foreign Key를 갖는다. 연결되는 테이블들 중 한 쪽의 테이블만 작성하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
+-------------+--------------+----------------------------+
|                        profile                          |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| gender      | varchar(255) |                            |
| photo       | varchar(255) |                            |
+-------------+--------------+----------------------------+

+-------------+--------------+----------------------------+
|                          user                           |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| name        | varchar(255) |                            |
| profileId   | int(11)      | FOREIGN KEY                |
+-------------+--------------+----------------------------+

2. one-to-many, many-to-one relations

참고: typeorm 공식 홈페이지

  • one-to-one과 달리 one-to-manymany-to-one@JoinColumn()을 생략할 수 있다.
  • one-to-many는 many-to-one의 기술이 선행되어야 한다. 반대로 many-to-one은 one-to-many의 기술 없이 독립적으로 사용가능하다.

아래의 예시는 사용자 한명이 여러 개의 사진을 갖는 관계를 갖는다. ( 사용자 : one, 사진 : many ) 사진 테이블에서 사용자 아이디 컬럼을 FK키로 갖는다.

one-to-many

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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[];
}

many인 photo의 타입은 Photo[], Photo의 array이다. (user.photos = [photo1, photo2])

many-to-one

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
+-------------+--------------+----------------------------+
|                         photo                           |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| url         | varchar(255) |                            |
| userId      | int(11)      | FOREIGN KEY                |
+-------------+--------------+----------------------------+

+-------------+--------------+----------------------------+
|                          user                           |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| name        | varchar(255) |                            |
+-------------+--------------+----------------------------+

many-to-many relations

참고: typeorm 공식 홈페이지

중간테이블을 만들어 다대다 관계를 설정할 수 있다. @JoinTable()은 한 쪽 테이블에만 정의하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import {
    Entity,
    PrimaryGeneratedColumn,
    Column
} from "typeorm";

@Entity()
export class Category {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import {
    Entity,
    PrimaryGeneratedColumn,
    Column,
    ManyToMany,
    JoinTable
} from "typeorm";
import { Category } from "./Category";

@Entity()
export class Question {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    title: string;

    @Column()
    text: string;

    @ManyToMany(() => Category)
    @JoinTable()
    categories: Category[];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+-------------+--------------+----------------------------+
|                        category                         |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| name        | varchar(255) |                            |
+-------------+--------------+----------------------------+

+-------------+--------------+----------------------------+
|                        question                         |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| title       | varchar(255) |                            |
| text        | varchar(255) |                            |
+-------------+--------------+----------------------------+

+-------------+--------------+----------------------------+
|              question_categories_category               |
+-------------+--------------+----------------------------+
| questionId  | int(11)      | PRIMARY KEY FOREIGN KEY    |
| categoryId  | int(11)      | PRIMARY KEY FOREIGN KEY    |
+-------------+--------------+----------------------------+

RelationOptions

참고 : Interface RelationOptions

cascade

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity()
export class Question {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    title: string;

    @Column()
    text: string;

    @ManyToMany(() => Category, { cascade : ["insert", "update"] })
    @JoinTable()
    categories: Category[];
}

onDelete

OnDeleteType: “RESTRICT” “CASCADE” “SET NULL” “DEFAULT” “NO ACTION”
1
2
3
4
5
6
7
8
9
10
11
12
@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;
    
    @OneToOne(() => Profile, { onDelete: 'CASCADE' })
    @JoinColumn()
    profile: Profile;
}

relation이 있는 테이블의 데이터가 삭제 됐을 때 데이터를 어떻게 처리할 지 지정할 수 있다.

예시 ) User 테이블의 user가 삭제됐을 때 user의 photo 데이터를 어떻게 처리할 수 있을까?

  • RESTRICT : user가 photo를 갖고 있을 때 user를 삭제할 수 없다.
  • CASCADE : user가 삭제되면 user의 photo도 삭제된다.
  • SET NULL : user가 삭제되면 user의 photo는 null값을 갖는다.
  • DEFAULT : user가 삭제되면 user의 photo는 default로 정해놓은 값을 갖는다.
  • NO ACTION : 옵션을 지정하지 않으면 갖는 default 옵션. user가 삭제되도 user의 photo는 삭제되지 않는다.