Nest.js - 단방향 1 to 1 Relationship in DB

크롱·2024년 10월 18일
0

Nest.js

목록 보기
6/15

단방향 1 to 1 Relationship ?

위 사진 처럼, 하나의 post는 meta_option 하나를 가지고있다. 하지만 meta_option은 post를 가지고있지 않으므로 post와 meta_option은 단방향성 1 to 1 관계를 가지고있다고 볼 수 있다.

✨ 단방향(unidirectional)과 양방향(bidirectional)이란?

단방향 관계
두 엔티티가 연관 관계를 맺고 있을 때, 한 쪽의 엔티티만 다른 쪽을 참조하고 있는 것을 의미합니다.
양방향 관계
두 엔티티가 연관 관계를 맺고 있을 때, 양 쪽이 서로를 참조하고 있는 것을 의미합니다.




TypeORM 실습

post.entity.ts 파일

import {
  Entity,
  PrimaryGeneratedColumn,
  Column,
  OneToOne,
  JoinColumn,
} from 'typeorm';
import { MetaOption } from 'src/meta-options/meta-option.entity';

@Entity()
export class Post {
  ...

  @OneToOne(() => MetaOption)
  @JoinColumn()
  metaOptions?: MetaOption;


}

🐤단방향 관계를 만드는 방법🐤
단방향의 주체 엔티티 파일에서 단방향이 이루어질 프로퍼티에 밑 두 데코레이터를 추가하면된다
@OneToOne(()=> Entity)
=> 인자로 객체 엔티티를 넘겨줌
@JoinColumn()
=> 엔티티 테이블에 컬럼을 만든다. 단방향 일때만 쓰인다.
위의 경우 Post 테이블에 meta options ID 컬럼을 만든다.

결과:

Repository 주입 실습

위 엔티티 구조도에서 봤듯이, Post 테이블에서는 meta_option이 필요하다. 만약 사용자가 post를 업로드할시 meta_options도 같이 업로드한다면, 먼저 meta_options를 db에 생성해야한다.

즉, PostsService 파일에서 meta_options DB 저장 -> post DB 저장

사실 이 방법은 코드가 길어진다. typeORM에서 cascade라는 더 좋은 방법이 있는데 이 방법을 먼저 알아본뒤 밑에 적기로한다.

module.ts

post를 생성할때 metaOptions DB와 post DB에 접근해야한다.
즉, postService.ts 에서 metaOptions와 post repository가 둘다 필요하므로 module 파일에서 둘의 엔티티를 imports 해야한다.

service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Post } from '../post.entity';
import { Repository } from 'typeorm';
import { MetaOption } from 'src/meta-options/meta-option.entity';
import { CreatePostDto } from '../dtos/createPost.dto';

@Injectable()
export class PostsService {
  constructor(
    @InjectRepository(Post)
    private readonly postsRepository: Repository<Post>,
    @InjectRepository(MetaOption)
    private readonly metaOptionsRepository: Repository<MetaOption>,
  ) {}

  public async create(createPostDto: CreatePostDto) {
    //Create metaOptions
    let metaOptions = createPostDto.metaOptions
      ? this.metaOptionsRepository.create(createPostDto.metaOptions)
      : null;

    if (metaOptions) { //1. 먼저 metaOptions DB 저장
      await this.metaOptionsRepository.save(metaOptions); //save 메소드를 이용해야지 DB에 저장됨
    }

    // Create post
    let post = this.postsRepository.create(createPostDto);

    // Add metaOptions to the post
    if (metaOptions) { // 2. post 레포에 위 생성한 metaOptions 할당
      post.metaOptions = metaOptions;
    }

    return await this.postsRepository.save(post); //3. post DB 저장
  }

 
}



repository.save 메소드에선 await 가 필요하다

create 메소드에서는 await를 쓰지않아도된다.
오직 save 메소드에서만 await가 필요한데, 프로미스를 반환하기때문.



🌈 간편하게 cascade 🌊

위에 코드는 PostsService에서 metaOptionsRepository과 postsRepository를 둘다 주입하고있는데 이러한 방법보다 훨씬 간편한 cascade를 알아보자

내가 원하는 작업?
post db 생성할때 meta_options도 db에 생성되는것.


entity 파일 cascade: true

import {
  Entity,
  ...
  OneToOne,
  JoinColumn,
} from 'typeorm';
import { MetaOption } from 'src/meta-options/meta-option.entity';

@Entity()
export class Post {
  ...
  @OneToOne(() => MetaOption, {
    cascade: true,
  })
  @JoinColumn()
  metaOptions?: MetaOption;

}

service 파일에선 더 간단하게


import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Post } from '../post.entity';
import { Repository } from 'typeorm';
import { CreatePostDto } from '../dtos/createPost.dto';

@Injectable()
export class PostsService {
  constructor(
    @InjectRepository(Post)
    private readonly postsRepository: Repository<Post>,
  ) {}

  public async create(createPostDto: CreatePostDto) {
    // Create post
    let post = this.postsRepository.create(createPostDto);

    return await this.postsRepository.save(post);
  }

기존에는 create 함수 안에서 metaOptionRepository를 주입하고 create,post까지 했는데 cascade:true를 설정하면 postsRepository에서 post만 create해도 metaOptions 까지 db에 저장된다.

post를 생성했는데 meta_option 테이블에도 잘 저장이 되었다.

eager / define relationship
eager= > 모든 테이블 다 가져와서 비효율적일수도

삭제 실습

What we are going to work on in this particular video is a sequential deletion, where we will first

delete the post and then we will delete the meta option.


eager / relations

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Post } from '../post.entity';
import { Repository } from 'typeorm';


@Injectable()
export class PostsService {
  constructor(
    @InjectRepository(Post)
    private readonly postsRepository: Repository<Post>,
  ) {}

  
  public async findAll(userId: string) {
    const posts = await this.postsRepository.find();
    return posts;
  }


}

post 테이블에있는 모든 데이터를 가져오고싶을때 postsRepository.find() 하면 된다. 하지만 리턴값을 보면 metaOptions가 없다. metaOptions은 다른 테이블에서 가져온것이기때문에 안불러와지는것같다. 이런경우

public async findAll(userId: string) {
    const posts = await this.postsRepository.find({
      relations: {
        metaOptions: true,
      },
    });
    return posts;
  }

find 메소드 안에 relations를 추가해서 원하는 프로퍼티를 작성한 뒤 true 해주면된다

잘가져온다.

또다른 방법으로 eager라는 것이 있는데, 이는 모든 프로퍼티를 다 가져와서 남용한다면 성능에 문제가생길수도있다.

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Post } from '../post.entity';
import { Repository } from 'typeorm';


@Injectable()
export class PostsService {
  constructor(
    @InjectRepository(Post)
    private readonly postsRepository: Repository<Post>,
  ) {}

  
  public async findAll(userId: string) {
    const posts = await this.postsRepository.find();
    return posts;
  }


}

------------------------------------------------------------
post.entity.ts

@Entity()
export class Post {
  ...
  @OneToOne(() => MetaOption, {
    cascade: true,
    eager: true,
  })
  @JoinColumn()
  metaOptions?: MetaOption;

}



참조: https://velog.io/@dnjscksdn98/JPA-Hibernate-Bidirectional-Unidirectional-Relationships

https://bhargavacharyb.medium.com/nestjs-5-understanding-entities-and-relationships-in-nestjs-with-typeorm-an-e-commerce-example-7cd3ee3d0174

profile
👩‍💻안녕하세요🌞

0개의 댓글