Published on

typeorm entities 에 대해서

Authors
  • avatar
    Name
    길재훈
    Twitter

typeorm entities 에 대해서 알아본다.

TypeormDocs 를 읽어보면서, 감탄이 나오는 부분이 바로 이 Entities 이다.

Entitydatabase tablecollectionmappingclass 이다.

Entities 를 사용하는데 있어서, Decorator 를 사용하는데,
매우 간편하게 만들어진다.

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'

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

  @Column()
  name: string

  @Column()
  isActive: boolean
}

매우 간편하게 만들어진다. 이렇게 만들어진 table 은 다음과 같다.

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

대단하지 않는가? 매우 명시적으로 code 작성이 가능하다.

이전의 Sequelize 를 잠깐 공부하고 있었는데, 보다 더 나은 방식으로 느껴진다.

여기서 Entity 를 사용하고 싶다면 앞전의 Data source 에 대해서entities options 를 사용하여 등록해주어야 한다.

import { DataSource } from "typeorm"

const dataSource = new DataSource({
    type: "mariadb",
    host: "localhost",
    port: 3306,
    username: "test",
    password: "test",
    database: "test",
    entities: ["entity/*.js"], // <-- entity directory 의 경로
})

또한 보통 Prefix 를 사용하여 지정도 하니, prefix option 사용도 고려하는것이 좋을것 같다.

추가적으로, Docs 에서는 constructor arguments 사용은 선택적이라고 말한다.

그 이유로는, database 를 로딩할때 entity classinstance 를 생성함으로 생성자 인수를 인식하지 못한다고 말한다.

이부분은 내부동작에 대한 원리를 아직은 모르므로, Docs 를 읽으면서 더 자세히 보아야 겠다.

Entity Columns

DB 상의 Columns 를 뜻한다.

TypeORM 의 각 Entity class property 들은 @Column 을 사용하여 table columnmapping 하도록 한다.

이러한 Columns 들은 여러종류가 있는데 각 종류에 대해서 보도록 한다.

@PrimaryColumn

Entity 는 반드시 하나의 Primary key 을 갖는다. 이러한 Primary key 를 가진 Column 을 설정하는 Decorator 이다.

import { Entity, PrimaryColumn } from "typeorm"

@Entity()
export class User {
    @PrimaryColumn()
    id: number
}

``Object.openSync (node:fs:601:3)
12:03:49.278	    at Object.readFileSync (node:fs:469:35)
12:03:49.278	

> 보통 `modeling` 시 어떠한 `table` 에 종속된 `column` 이 있을때 정규화시켜, `parent table id``child table id` 를 합쳐서 `primary key` 로 만들기도 한다.

```ts
import { Entity, PrimaryColumn } from "typeorm"

@Entity()
export class User {
    @PrimaryColumn()
    firstName: string

    @PrimaryColumn()
    lastName: string
}

단순하게 이렇게, @PrimaryColumn 을 2개 만들어주면 된다.

이제 idprimary key 를 가진 id 가 된다.

@PrimaryGeneratedColumn

Primary key 생성은 알겠다.
하지만 보통은 Primary keyInt 형식으로 만들고,
AUTO_INCREMENT 형식으로 자동 증분 되도록 생성하기도 한다.

이러한 기능을 넣어 만든 decorator@PrimaryGeneratedColumn 이다.

import { Entity, PrimaryGeneratedColumn } from "typeorm"

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

이제 id 는 자동증분되는 primary key 이다.

@PrimaryGeneratedColumn("uuid")

Primary keyAUTO_INCREMENT 방식으로 생성하기도 하지만, 이는 단순히 1 부터 값이 증가하는 방식으로 구성된다.

이렇게 만들지 않고, 특별한 중복되지 않는 key 를 사용하여 만들수 있는데 그것이 uuid 이다.

uuid 는 중복되지 않는 key 이므로, primary key 로 작성 가능하다.

uuid 로 작성하고 싶다면, @PrimaryGeneratedColumn 의 인자로 uuid 문자열을 넣어주면 된다.

import { Entity, PrimaryGeneratedColumn } from "typeorm"

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

@CreateDateColumn

생성시 자동적으로 time 을 추가해주는 Decorator 이다.

@Entity()
export class User {
    @CreateDateColumn()
    createdDate: Date
}

@CreateDateColumn

EntitySave 할때마다, 해당 Columntime
업데이트 해주는 Column 이다.

@Entity()
export class User {
    @UpdateDateColumn()
    updatedDate: Date
}

@DeleteDateColumn

Sofe-delete 시에 Deletetime 을 자동적으로 설정 해주는 Column 이다.

만약, @DelateDateColumn 이 설정되어 있다면, TypeORM 의 기본 scopenon-delete 가 될것이라고 한다.

@Entity()
export class User {
    @DeleteDateColumn()
    deletedDate: Date
}

@VersionColumn

자동적으로 Versioning 해주는 Column 이다. 여기서는 숫자를 증분해주어서 자동적으로 설정해주는듯 하다.

자동적으로 증분되는 조건은 save 가 호출될때, 마다 설정해준다.

@Entity()
export class User {
    @VersionColumn()
    version: number
}

Spatial Columns

거의 왠만한 RDBMS 들은 spatial columns 를 지원한다. TypeORM 역시 이러한 부분을 제공해주는듯 하다.

MySQL/MariaDBgeometiresWKT(Well-Known Text) 를 제공한다고 한다.

그래서 Geometry ColumnsString 타입과 함께 tagged 된다.

import { Entity, PrimaryColumn, Column } from "typeorm"

@Entity()
export class Thing {
    @PrimaryColumn()
    id: number

    @Column("point")
    point: string

    @Column("linestring")
    linestring: string
}

...

const thing = new Thing()
thing.point = "POINT(1 1)"
thing.linestring = "LINESTRING(0 0,1 1,2 2)"

반면, PostgreSQL and CockroachDB 같은경우는 GeoJSON 포멧으로 변환되므로, Object 또는 Geometry(or subclasses, eg Point) 방식으로 제공된다고 한다.

import {
    Entity,
    PrimaryColumn,
    Column,
    Point,
    LineString,
    MultiPoint
} from "typeorm"

@Entity()
export class Thing {
    @PrimaryColumn()
    id: number

    @Column("geometry")
    point: Point

    @Column("geometry")
    linestring: LineString

    @Column("geometry", {
        spatialFeatureType: "MultiPoint",
        srid: 4326,
    })
    multiPointWithSRID: MultiPoint
}

...

const thing = new Thing()
thing.point = {
    type: "Point",
    coordinates: [116.443987, 39.920843],
}
thing.linestring = {
    type: "LineString",
    coordinates: [
        [-87.623177, 41.881832],
        [-90.199402, 38.627003],
        [-82.446732, 38.413651],
        [-87.623177, 41.881832],
    ],
}
thing.multiPointWithSRID = {
    type: "MultiPoint",
    coordinates: [
        [100.0, 0.0],
        [101.0, 1.0],
    ],
}

이 부분은 Geometry 관련해서, data 저장시 다시 살펴보도록 한다.

Column Types

지금까지 Column 의 종류를 보았고,
Column 에 사용될 Data Type 에 대해서 살펴본다.

Colume Types 를 지정하기 위해서는 Column Decorator
인자로 Type 을 문자열로 넣어준다.

@Column("int")
// or
@Column({ type: "int" })
// or
@Column("varchar", { length: 200 })
// or
@Column({ type: "int", width: 200 })

이러한 Type 이 존재하지만, 이는 DB 의 종류에 따라
Type 이 달라지기도 한다.

이부분에 대해서는 Column types for mysql/mariadb 에서 보도록 한다.

enum Column Type

mysqlpostgres 에서는 emnum 타입을 지원한다. 이를 사용하기 위해서는 다음과 같은 문법을 사용한다.

export enum UserRole {
    ADMIN = "admin",
    EDITOR = "editor",
    GHOST = "ghost",
}

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

    @Column({
        type: "enum",
        enum: UserRole,
        default: UserRole.GHOST,
    })
    role: UserRole
}

Column 은 총 3가지 타입을 지원해주며, 이는 다음과 같다.

  • type:
    Column 의 타입 지정

  • enum:
    enum 으로 선언된 변수를 넣어준다.

  • default:
    enum 으로 넣어준 변수에서 default 로 사용될 값을 지정한다.

또는, enum option 에 배열을 넣어주어서 사용도 가능하다.

export type UserRoleType = "admin" | "editor" | "ghost",

@Entity()
export class User {

    @PrimaryGeneratedColumn()
    id: number;

    @Column({
        type: "enum",
        enum: ["admin", "editor", "ghost"],
        default: "ghost"
    })
    role: UserRoleType
}

set Column Type

mariadbset 타입역시 지원한다.

export enum UserRole {
    ADMIN = "admin",
    EDITOR = "editor",
    GHOST = "ghost",
}

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

    @Column({
        type: "set",
        enum: UserRole,
        default: [UserRole.GHOST, UserRole.EDITOR],
    })
    roles: UserRole[]
}

이 역시, enumArray 로 받아서 처리가능하다.

export type UserRoleType = "admin" | "editor" | "ghost",

@Entity()
export class User {

    @PrimaryGeneratedColumn()
    id: number;

    @Column({
        type: "set",
        enum: ["admin", "editor", "ghost"],
        default: ["ghost", "editor"]
    })
    roles: UserRoleType[]
}

simple-array Column type

TypeORMsimple-array 라는 특별한 Column 을 지원한다.

이는 Array 인듯 하지만, 실제로 저장되는 데이터는 Comma 를 가진 문자열로 저장된다.

정규형 공부할때 보는, Comma 로 이어진 다가속성 처럼 생겼다.

이를 이해하기 위해 다음을 보도록 하자.


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

    @Column("simple-array")
    names: string[]
}

위의 예시에서 보면 namesstring[] 타입이며,
Columnsimple-array 이다.

만약 이러한 names 에 배열을 집어 넣어보자.

const user = new User()
user.names = ["Alexander", "Alex", "Sasha", "Shurik"]

이때, 저장되는 데이터는 다음처럼 저장된다.

Alexander,Alex,Sasha,Shurik

names 컬럼에 들어가는 값이 마치 다가속성처럼 저장되는 것이다.

이러한 부분을 많이 사용하는지는 아직 미지수이다. 하지만, Programing 입장에서 보면, 배열값이 한정적으로 사용된다면, 괜찮은 방식일수 있겠다는 생각도 같이 든다.

주의사항

작성하는 값에 Comma 가 없어야 제대로 동작한다.

simple-json Column type

simple-array 타입도 보았는데, json 이 있다고 해도 별로 놀랍지는 않다.

이는 DBJSON.stringify 로 저장되게 할 수 있다. 즉, 문자열로 저장된다는 것이다.

생각해보면 꽤나 간단하게 구현가능할것 같기도 하다. 머리를 잘썼다.

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

    @Column("simple-json")
    profile: { name: string; nickname: string }
}

이는 다음처럼 사용된다.

const user = new User()
user.profile = { name: "John", nickname: "Malkovich" }

그리고 저장되는 값은 다음과 같다.

"{"name":"John","nickname":"Malkovich"}" 

이제 DB 로 부터 해당 값을 가져온다면, JSON.parse 를 통해서
그 값은 Object/array/primitive 값으로 받게 될것이다.

Columns with generated values

@Generated 데커레이터가 존재하는데, 이 데커레이터는 생성된 값으로 해당 열에 자동적으로 값 주입이 가능하다.

@Entity()
export class User {
    @PrimaryColumn()
    id: number

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

이제 uuid 컬럼은 자동적으로 생성된 uuid 값을 가진다.

이 외에도 increment, identity(postgres), rowid(cockroachDB) 를 줄수 있는데,
이러한 유형으로 만들어진 이유는 각 DB 마다 지원하는 generated 타입이 다르기 때문이다.

Column options

이렇게 Column Types 를 보았으니, Column options 를 보도록 한다.

  • type: ColumnType
    Column 에 사용될 타입을 지정한다.

  • name: string
    DB TableColumn 의 이름을 지정한다.

  • length: number
    Column Typelength 값을 지정한다.
    만약, varchar 타입에 200length 값을 지정하고 싶다면
    @Column({type: 'varchar', length: 200}) 으로 지정가능하다.

  • width: number Column 타입의 width 를 표시한다.
    이는 number 타입에 해당하는 것으로, 허용 가능한 값의 범위를 뜻하는듯 하다.
    오직 MySQL integer types 를 위해 사용된다고 한다.

이부분은 명확하게 맞는지 모르겠다. 다른 SQL 들도 값의 범위가 있을텐데?

  • onUpdate: string
    이는 ConstraintON UPDATEtrigger 한다.

  • nullable: boolean
    True 이면 Null 을 허용하지만, False 이면 NOT NULL 로 허용하지 않는다. defaultnullable: false 이다.

  • update: boolean
    save 작업에 의해 update 되는지 허용 여부를 결정한다.
    만약 false 라면 object 에 처음 insert 될때만 쓰기 가능하다. (즉, update 가 안되다는 말이다...)
    Default 값은 True 이다.

  • insert: boolean
    Object 에 처음 insert 될때, Column 값이 설정되어 있는지 여부를 알려준다.
    Default 값은 True 이다.

  • select: boolean
    query 를 만들때, 기본적으로 이 Column 을 숨길지 아닐지 결정한다. false 로 설정될떼, 이 Column data 가 표준 쿼리와 함께 표시되지 않을 것이다.
    Defaultselect: true 이다.

  • default: string
    Default 값을 할당한다.

  • primary: boolean
    Primary 로 표시한다.
    이는 @PrimaryColumn 과 같다.

  • unique: boolean
    Columnunique한 값으로 만든다.

  • comment: string
    Column 의 설명을 달 수 있다.
    모든 데이터베이스에서 지원하는 부분은 아니라고 한다.

  • precision: number
    deciaml 의 정밀도를 지정한다.

  • zerofill: boolean
    숫자 컬럼에 ZEROFILL 속성을 넣는다.
    이는 MySQL 에서 사용가능하며, true 이면, UNSIGNED 속성의 컬럼이 되며 자동적으로 추가된다.

  • unsigned: boolean
    UNSIGNED 속성이 추가되며 숫자 컬럼이 부호없는 숫자가 된다.

  • charset: string
    원하는 Character set 지정이 가능하다.
    모든 DB타입에서 지원하지는 않는다고 말한다.

  • collation: string
    Columncollation 을 지정한다.

  • enum: string[] | AnyEnum
    enum 컬럼의 타입을 지정할때 사용된다.
    지정한 enum 혹은 값의 array 를 지정할 수 있다.

  • enumName: string
    enum 을 사용한다면 그 이름을 지정한다.

  • asExpression: string
    Generated column expression 방식이라는데, 해당 문법은 현재 나도 생소해서 추가적으로 MYSQL 을 보고 공부한후 이해해야할 것 같다.

  • generatedType: "VIRTUAL" | "STORED"
    Generated column type 이라는데, 이부분역시 생소하다.

  • hstoreType: "object"|"string"
    Postgres 에서 사용하는 TypeHSTORE 컬럼을 사용한다고 한다.

  • array: boolean
    PostgresCockroachDBarray 컬럼타입을 사용한다.

  • transformer
    임의의 EntityType 속성을 DB 에서 지원하는 DB Type 유형으로 변환하는데 사용한다고 한다.
    (여기서 이를 marshal 이라고 표현하는데, 이 뜻이 변환을 말하는것 같다.. )
    이부분은 조금 더 살펴보아야 겠다.
    다른 유용한 방식으로 사용하는 것 같기도 하고 애매하다..

Entity inheritance

EntityClass 이다. 즉, 상속 가능하다!!

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
}

Tree entities

TypeORMAdjacency listClosure tabletree structure 저장 패턴으로 지원한다.

Tree entities 에 대해서는 기본적인 개념을 알고 보아야 할것같다. 이부분에 대해서는 추가적인 내용으로 공부내용을 작성할 생각이다.

Adjacency list

Adjacency list(인접 목록) 은 그냥 자체 참조가 가능한 모델이다.

이 방식은 단순하지만, Join limiations 로 인해 Big tree 를 한번에 load 하지 못하는 것이 단점이라고 설명하고 있다.

import {
    Entity,
    Column,
    PrimaryGeneratedColumn,
    ManyToOne,
    OneToMany,
} from "typeorm"

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

    @Column()
    name: string

    @Column()
    description: string

    @ManyToOne((type) => Category, (category) => category.children)
    parent: Category

    @OneToMany((type) => Category, (category) => category.parent)
    children: Category[]
}

Closure table

Closure table 은 기존의 자기참조 relationship 에 대한
대책으로써 만들어진 table 방법이다.

이는 기존의 자손과 조상이라는 개념을 사용하여, 가장 상단의 Table 부터 아래 마지막 자손 Table 까지의 관계도를 Table 로 만들어, 원하는 Column 을 더 효율적으로 검색하기 위한 방법이다.

이러한 방식을 지원하기 위해 TypeORM 은 다음처럼 작성하기를 원한다.

import {
    Entity,
    Tree,
    Column,
    PrimaryGeneratedColumn,
    TreeChildren,
    TreeParent,
    TreeLevelColumn,
} from "typeorm"

@Entity()
@Tree("closure-table")
export class Category {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    name: string

    @Column()
    description: string

    @TreeChildren()
    children: Category[]

    @TreeParent()
    parent: Category

    @TreeLevelColumn()
    level: number
}

여기서 levelClosure TableDepth 를 말하는 듯 하다.

이 부분에 대한 개념정도는 이해한 부분으로, 이 Logic 이 어떻게 이루어져 있을지 머릿속으로 잘 그려지지는 않는다.

일단, 이러한 부분을 제공하며 사용할 수 있다는 정도로 넘어가고, 더 자세한 부분은 SQL 방법론들을 보면서 익히도록 하는것이 좋을 듯 싶다.

Embedded Entities

TypeORMClass inheritance 뿐만 아니라,
Enitity 를 내장할 수 있는 방법역시 제공한다.

다음을 보자.

import { Column } from "typeorm"

export class Name {
    @Column()
    first: string

    @Column()
    last: string
}

위는 Name 이라는 Column 으로 이루어진 Class 이다.
중요한건 Entity 데커레이터를 사용하지 않았다는 점이다.

이는 Embedded Column 으로 사용하기 위한 초석이다.

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

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

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

    @Column()
    isActive: boolean
}

보았는가? 위는 Name 클래스를 가져온후, Column 데커레이터에 함수로 Name 을 전달하여, name 컴럼을 만들었다.

이때, TypeORMcolumsconnect 할 수 있다고 표현한다.

이제 해당 ColumeTable 로 만들어 보면 다음처럼 이루어져 있다.

+-------------+--------------+----------------------------+
|                          user                           |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| nameFirst   | varchar(255) |                            |
| nameLast    | varchar(255) |                            |
| isActive    | boolean      |                            |
+-------------+--------------+----------------------------+

이는 마치 컴포넌트를 재사용하는 방식처럼 자주 사용되는 Column 을 만들어 사용하기 용이한듯 싶다.

Concrete Table inheritance(구상 테이블 상속)

코드의 중복을 줄이는데 상속 만큼 좋은것은 없다.
다음을 보자.

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

    @Column()
    title: string

    @Column()
    description: string

    @Column()
    size: string
}

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

    @Column()
    title: string

    @Column()
    description: string

    @Column()
    answersCount: number
}

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

    @Column()
    title: string

    @Column()
    description: string

    @Column()
    viewCount: number
}

딱 보아도, id, title,description 이 중복됨을 보인다. 앞에서는 Embedded Column 을 사용하여 중복됨을 피했지만,

abstract class 를 사용하여, 중복되는 구체적 로직들을 상속하여 처리 가능하다.

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
}

부모로 부터 상속받아, Entity 로 만든다.

Single Table inheritance

TypeORMConcrete Table 이 아닌, Single Table 로 부터의 상속도 지원한다.

이때 중요한 부분은 @TableInheritance 데커레이터를 사용하여, 상속을 받을 Single Table 을 선언하고, 이후 @childEntity 를 사용하여 Single Table 의 자식으로 들어갈 Class 를 만든다.

@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
}

이제 photos, questions, postscontents 테이블안에 save 된다.

보면서 아래의 구문이 약간은 이상하게 보인다..

@TableInheritance({ column: { type: "varchar", name: "type" } })

하지만, 잘 생각해보면, 상속받을 column 의 타입이 varchar 이며 이름은 type 을 가지면, 상속받을 수 있도록 지정한것으로 보인다.

그러므로 모든 @Column 은 내부적으로 type 을 가지므로,
모든 Column 을 상속받는다는 뜻으로 이해하고 있다.

View Entity

Database ViewmappingClass 이다. 이를 위해서 @ViewEntity 데커레이터를 사용하면 쉼게 새로운 Class 를 정의할 수 있다.

@ViewEntity 는 몇가지 옵션들이 존재하는데 다음과 같다.

  • name
    view 의 이름이다.
    이름을 지정하지 않는다면 entity class name 으로 생성된다.

  • database
    DB server 안의 database 이름이다.

  • schema
    Schema 이름이다.

  • expression
    view 를 정의할 표현식이다.
    이는 반드시 정의되어야한다.

  • dependsOn
    현재 view 가 의존하는 다른 view 들의 list 를 말한다.
    만약 view 에 정의된것이 다른 view 를 사용한다면,
    올바르게 migration 될 수 있도록 올바른 순서로 정의해야 한다.

위의 expression 은 다음처럼 작성할 수 있다.

@ViewEntity({
    expression: `
        SELECT "post"."id" AS "id", "post"."name" AS "name", "category"."name" AS "categoryName"
        FROM "post" "post"
        LEFT JOIN "category" "category" ON "post"."categoryId" = "category"."id"
    `
})

또는 QueryBuilder 를 사용하여 정의가능하다.

@ViewEntity({
    expression: (dataSource: DataSource) => dataSource
        .createQueryBuilder()
        .select("post.id", "id")
        .addSelect("post.name", "name")
        .addSelect("category.name", "categoryName")
        .from(Post, "post")
        .leftJoin(Category, "category", "category.id = post.categoryId")
})

이렇게 생성된 viewDataSourceentities 배열에 등록되어야 한다.

import { DataSource } from "typeorm"
import { UserView } from "./entity/UserView"

const dataSource = new DataSource({
    type: "mysql",
    host: "localhost",
    port: 3306,
    username: "test",
    password: "test",
    database: "test",
    entities: [UserView],
})

자, 이제 해당 @ViewEntity 를 사용할 Class 를 보도록 하자.

import { ViewEntity, ViewColumn } from "typeorm"

@ViewEntity({
    expression: `
        SELECT "post"."id" AS "id", "post"."name" AS "name", "category"."name" AS "categoryName"
        FROM "post" "post"
        LEFT JOIN "category" "category" ON "post"."categoryId" = "category"."id"
    `,
})
export class PostCategory {
    @ViewColumn()
    id: number

    @ViewColumn()
    name: string

    @ViewColumn()
    categoryName: string
}

위는 @ViewEntity 에서 정의한 expression 에 따라,
@ViewColumn 데커레이터를 사용한것을 볼 수 있다.

QueryBuilder 를 사용한 @ViewEntity 는 다음과 같다.

import { ViewEntity, ViewColumn } from "typeorm"

@ViewEntity({
    expression: (dataSource: DataSource) =>
        dataSource
            .createQueryBuilder()
            .select("post.id", "id")
            .addSelect("post.name", "name")
            .addSelect("category.name", "categoryName")
            .from(Post, "post")
            .leftJoin(Category, "category", "category.id = post.categoryId"),
})
export class PostCategory {
    @ViewColumn()
    id: number

    @ViewColumn()
    name: string

    @ViewColumn()
    categoryName: string
}

View Column options

@ViewColomn 에도 여러 옵션들이 존재한다.

  • name: string
    Column 이름을 지정한다.

  • transformer
    지원가능한 Type 으로 변경해주는 변경자인듯 한데,
    이부분은 잘 와닫지 않아서 추후 더 살펴볼 예정이다.

Seperating Entity Definition

해당 부분은 Sequelizedefine 처럼 사용가능하도록
만든 문법 같은데 많이 사용하지는 않을 것 같아서,
내용을 정리하지는 않는다.

혹시몰라 Seperating Entity Definition 여기세서 볼 수 있도록 링크는 남기도록 한다.

마무리

확실히 API 가 참 많기도 하며, 기본적인 개념이 밑바탕이 되어야 이해가 갈만한 내용이 많다.

특히 Tree Entities 부분은, 기본적인 개념을 밑바탕이 있어야
이해가 수월할 듯 싶다.

다음은, Relations 관련 내용을 정리하도록 한다.

오늘내로 내용 정리이후에, API 코드 작성을 목표로 하고 있는데, 아직 이해해야할 부분이 많으므로 최대한 목표로 삼은 부분까지 공부하도록 할것이다.