import {PrimaryGeneratedColumn} from "typeorm";
export class Name {
@Column()
first: string
@Column()
last: string
}
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: string
@Column(()=> Name)
name: Name
@Column()
isActive: boolean
}
@Entity()
export class Employee {
@PrimaryGeneratedColumn()
id: string
@Column(()=> Name)
name: Name
@Column()
salary: number
}
| USER | ||
|---|---|---|
| id | int | PRIMARY KEY AUTO_INCREMENT |
| nameFirst | varchar | |
| nameLast | varchar | |
| isActive | boolean |
| employee | ||
|---|---|---|
| id | int | PRIMARY KEY AUTO_INCREMENT |
| nameFirst | varchar | |
| nameLast | varchar | |
| salary | boolean |
✏️
embed된 컬럼은class 명+컬럼명순으로 생성된다
@Entity()
export class Movie{
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
genre: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
@VersionColumn()
version: number;
}
{
"title": "반지의 제왕",
"genre": "fantasy",
"id": 1,
"createdAt": "2024-11-27T03:17:52.708Z",
"updatedAt": "2024-11-27T03:17:52.708Z",
"version": 1
}
✏️ 기존
MOVIE엔티티와 생성되는 데이터 형식
import {Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, VersionColumn} from "typeorm";
export class BaseEntity{
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
@VersionColumn()
version: number;
}
@Entity()
export class Movie{
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
genre: string;
@Column(() => BaseEntity)
base: BaseEntity;
}
{
"title": "반지의 제왕",
"genre": "fantasy",
"id": 1,
"base": {
"createdAt": "2024-11-27T03:17:52.708Z",
"updatedAt": "2024-11-27T03:17:52.708Z",
"version": 1
}
}
✔️ 같은 테이블이지만 객체가 들어가 있다고 인식된다.
import {Column, PrimaryGeneratedColumn} from "typeorm";
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 Post extends Content {
@Column()
viewCount: number
}
| Photo | ||
|---|---|---|
| id | int | PRIMARY KEY AUTO_INCREMENT |
| title | varchar | |
| description | varchar | |
| size | boolean |
| Post | ||
|---|---|---|
| id | int | PRIMARY KEY AUTO_INCREMENT |
| title | varchar | |
| description | varchar | |
| viewCount | number |
import {Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, VersionColumn} from "typeorm";
export abstract class BaseEntity{
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
@VersionColumn()
version: number;
}
@Entity()
export class Movie extends BaseEntity{
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
genre: string;
}
{
"title": "반지의 제왕",
"genre": "fantasy",
"createdAt": "2024-11-27T03:25:18.406Z",
"updatedAt": "2024-11-27T03:25:18.406Z",
"version": 1,
"id": 1
}
import {Entity, PrimaryGeneratedColumn} from "typeorm";
@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 Post extends Content {
@Column()
viewCount: number
}
| Content | ||
|---|---|---|
| id | int | PRIMARY KEY AUTO_INCREMENT |
| type | varchar | |
| title | varchar | |
| description | varchar | |
| size | varchar | Null |
| viewCount | varchar | Null |
import {ChildEntity, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, TableInheritance, UpdateDateColumn, VersionColumn} from "typeorm";
export abstract class BaseEntity{
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
@VersionColumn()
version: number;
}
// movie / series -> Content
// runtime (영화 상영시간) / seriesCount (몇개 부작인지)
@Entity()
@TableInheritance({
column: {
type: 'varchar',
name: 'type'
}
})
export class Content extends BaseEntity{
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
genre: string;
}
@ChildEntity()
export class Movie extends Content{
@Column()
runtime: number;
}
@ChildEntity()
export class Series extends Content{
@Column()
seriesCount: number;
}
✏️
app.module에entity추가
// app.module
import { Module } from '@nestjs/common';
import { MovieModule } from './movie/movie.module';
import {TypeOrmModule} from "@nestjs/typeorm";
import {ConfigModule, ConfigService} from "@nestjs/config";
import * as Joi from "joi";
import {Content, Movie, Series} from "./movie/entity/movie.entity";
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // ConfigModule 에 정의한 환경변수들을 어떤 모듈에서든 사용가능하게 함
validationSchema: Joi.object({
ENV: Joi.string().valid('dev', 'prod').required(),
DB_TYPE: Joi.string().valid('postgres').required(),
DB_HOST: Joi.string().required(),
DB_PORT: Joi.number().required(),
DB_USERNAME: Joi.string().required(),
DB_PASSWORD: Joi.string().required(),
DB_DATABASE: Joi.string().required(),
}),
}),
TypeOrmModule.forRootAsync({
useFactory: (configService: ConfigService) => ({
type: configService.get<string>("DB_TYPE") as "postgres",
host: configService.get<string>("DB_HOST"),
port: configService.get<number>("DB_PORT"),
username: configService.get<string>("DB_USERNAME"),
password: configService.get<string>("DB_PASSWORD"),
database: configService.get<string>("DB_DATABASE"),
entities: [
Movie, Series, Content
],
synchronize: true,
}),
inject: [ConfigService]
}),
MovieModule
],
})
export class AppModule {}
✏️
Movie.module에도 추가.
import { Module } from '@nestjs/common';
import { MovieService } from './movie.service';
import { MovieController } from './movie.controller';
import {TypeOrmModule} from "@nestjs/typeorm";
import {Movie, Series} from "./entity/movie.entity";
@Module({
imports:[TypeOrmModule.forFeature([
Movie, Series
])],
controllers: [MovieController],
providers: [MovieService],
})
export class MovieModule {}
테스트를 위한
Service와Controller수정
// `movie.service`
async create(createMovieDto: CreateMovieDto) {
return await this.movieRepository.save({
...createMovieDto,
runtime: 100
});
}
// 테스트용
async createSeries(createMovieDto: CreateMovieDto){
return await this.seriesRepository.save({
...createMovieDto,
seriesCount: 100,
})
}
// movie.controller
@Post()
create(@Body() body: CreateMovieDto) {
return this.movieService.create(body);
}
@Post('series')
createSeries(@Body() body: CreateMovieDto) {
return this.movieService.createSeries(body);
}
{
"title": "반지의 제왕",
"genre": "fantasy",
"runtime": 100,
"createdAt": "2024-11-27T03:43:28.490Z",
"updatedAt": "2024-11-27T03:43:28.490Z",
"version": 1,
"id": 2
}
✏️
movie생성
{
"title": "파괴의 제왕",
"genre": "fantasy",
"seriesCount": 100,
"createdAt": "2024-11-27T03:43:22.157Z",
"updatedAt": "2024-11-27T03:43:22.157Z",
"version": 1,
"id": 1
}
✏️
Series생성
List of relations
Schema | Name | Type | Owner
--------+---------+-------+--------
public | content | table | myuser
(1 row)
✏️
content라는 테이블 하나만 존재함.
createdAt | updatedAt | version | id | title | genre | runtime | seriesCount | type
----------------------------+----------------------------+---------+----+-------------+---------+---------+-------------+--------
2024-11-27 12:43:22.157592 | 2024-11-27 12:43:22.157592 | 1 | 1 | 파괴의 제왕 | fantasy | | 100 | Series
2024-11-27 12:43:28.490796 | 2024-11-27 12:43:28.490796 | 1 | 2 | 반지의 제왕 | fantasy | 100 | | Movie
(2 rows)
✏️
type컬럼에Movie | Series를 구분할 수 있도록 값이 들어가 있고,runtime과seriesCount는nullable로 설정된 모습.
✏️
임베디드 엔티티
반복적으로 사용되는 속성을 재사용하려는 경우 사용되며,
데이터베이스 테이블에 독립적인 테이블로 생성되지 않고, 사용하는 엔티티의 컬럼들로 직접 포함됨.
✏️
상속 기반 엔티티(Entity Inheritance)
공통 속성이 있지만, 개별적인 테이블로 데이터를 관리하려는 경우 사용되며,
추상 클래스로 설정되어 독립적인 테이블로 생성되지 않는다.
상속받은 엔티티들은 각각 별도의 테이블로 생성되며, 추상 클래스의 모든 속성을 상속받는다.
✏️
단일 테이블 상속(Single Table Inheritance)
공통 속성이 있고, 데이터를 하나의 테이블에서 관리하려는 경우 사용한다.
테이블 하나만 생성되며, 각 행은type컬럼에 의해 구분된다.