엔터티

ClassBinu·2024년 5월 19일

F-lab

목록 보기
26/65

기본

ORM은 데이터베이스 테이블과 객체 지향 프로그래밍의 클래스 간에 매핑을

When using an entity constructor its arguments must be optional. Since ORM creates instances of entity classes when loading from the database, therefore it is not aware of your constructor arguments.

엔터티에 생성자가 있으면 이건 무조건 선택적이어야 한다.
왜냐면 ORM은 이 과정에서 기본적으로 생성자를 호출하게 되는데, 생성자가 필수 인수를 요구한다면 ORM이 이러한 요구사항을 충족시키기 어렵습니다.

save 메서드를 알게 되었다

pk 이를 사용하여 엔터티를 저장할 때 save항상 주어진 엔터티 ID(또는 ID)를 사용하여 데이터베이스에서 엔터티를 찾으려고 시도합니다. ID/ID가 발견되면 데이터베이스에서 이 행을 업데이트합니다. 해당 ID가 포함된 행이 없으면 새 행이 삽입됩니다.

메서드 사용 예시

const user = await dataSource.manager.findOneBy(User, {
    firstName: "Timber",
    lastName: "Saw",
})
const user = await dataSource.getRepository(User).findOneBy({
    firstName: "Timber",
    lastName: "Saw",
})

특수 컬럼

  • @CreateDateColumn
  • @UpdateDateColumnsave
  • @DeleteDateColumn
  • @VersionColumnsave

Delete하면 시간을 왜 기록하지?

SoftDelete에 사용함

await repository.softDelete(id);

const user = await repository.findOneBy({ id: userId });
await repository.softRemove(user);

컬럼 타입

nullable, default 등등..

Embedded Entities

엔터티 간에 반복되는 컬럼을 약간 템플릿 처럼 만들어서 쓰는 개념?

import { Column } from "typeorm"

export class Name {
    @Column()
    first: string

    @Column()
    last: string
}

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
import { Name } from "./Name"

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: string

    @Column(() => Name)
    name: Name

    @Column()
    isActive: boolean
}

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
import { Name } from "./Name"

@Entity()
export class Employee {
    @PrimaryGeneratedColumn()
    id: string

    @Column(() => Name)
    name: Name

    @Column()
    salary: number
}

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
import { Name } from "./Name"

@Entity()
export class Student {
    @PrimaryGeneratedColumn()
    id: string

    @Column(() => Name)
    name: Name

    @Column()
    faculty: string
}

계속해서 중첩해서 할 수도 있음.
이렇게 단순 반복적인 엔터티는 임베디트 엔터티를 사용하면 편리함.

Entity Inheritance

중복을 줄이기 위해 말 그대로 엔터티 자체를 상속

export abstract class Content {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    title: string

    @Column()
    description: string
}
@Entity()
export class Photo extends Content {
    @Column()
    size: string
}
@Entity()
export class Question extends Content {
    @Column()
    answersCount: number
}
@Entity()
export class Post extends Content {
    @Column()
    viewCount: number
}

Single Table Inheritance

모든 인스턴스가 단일 테이블에 저장된다.
(데코레이터 잘 보기)

@Entity()
@TableInheritance({ column: { type: "varchar", name: "type" } })
export class Content {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    title: string

    @Column()
    description: string
}
@ChildEntity()
export class Photo extends Content {
    @Column()
    size: string
}
@ChildEntity()
export class Question extends Content {
    @Column()
    answersCount: number
}
@ChildEntity()
export class Post extends Content {
    @Column()
    viewCount: number
}

Tree Entities

이건 우선 넘어가기
나중에 코드로 필요할 때 살펴보자

View Entities

데이터베이스 뷰는 하나 이상의 테이블에서 유도된, 저장된 쿼리의 결과로 만들어진 가상 테이블
즉, 미리 복잡한 쿼리를 뷰로 짜 놓고,
서비스단에서는 단순한 호출로 복잡한 쿼리를 추상화해서 호출할 수 있음.

뷰는 읽기 전용이다!

import { ViewEntity, ViewColumn, Connection } from 'typeorm';

@ViewEntity({
    expression: (connection: Connection) => connection.createQueryBuilder()
        .select("user.id", "id")
        .addSelect("user.name", "name")
        .from(User, "user")
        .where("user.registered = :registered", { registered: true })
})
export class ActiveUser {
    @ViewColumn()
    id: number;

    @ViewColumn()
    name: string;
}

이렇게 호출함.

import { getRepository } from 'typeorm';
import { ActiveUser } from './ActiveUser';

async function getActiveUsers() {
    const userRepository = getRepository(ActiveUser);
    const activeUsers = await userRepository.find();
    console.log(activeUsers);
}

Separating Entity Definition

음.. 굳이..?
스키마 확장에 장점이 있구나
이건 나중에 클래스로 정의하는 게 진짜 익숙해지면 심화로 살펴보기

0개의 댓글