Transaction의 문제점

image.png

Lost Reads

image.png

Dirty Reads

image.png

Non-repeatable Reads

image.png

Phantom Reads

image.png

Transaction Level & Transaction Anomaly

image.png

사용 예시

async create(dto: CreateMovieDto) {
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      const { directorId, detail, genreIds, ...movieInfo } = dto;
      const director = await queryRunner.manager.findOne(Director, {
        where: { id: directorId },
      });
      if (!director) {
        throw new NotFoundException('존재하지 않는 ID의 감독입니다.');
      }
      const genres = await queryRunner.manager.find(Genre, {
        where: { id: In(genreIds) },
      });
      if (genres.length !== genreIds.length) {
        throw new NotFoundException(
          `존재하지 않는 장르가 있습니다. 존재하는 ids => ${genres.map((genre) => genre.id).join(',')}`,
        );
      }
      // cascade: true 옵션을 주면 영화 상세 정보를 생성할 때 영화 정보도 함께 생성된다.
      const movie = await queryRunner.manager.save(Movie, {
        ...movieInfo,
        detail: {
          detail: detail,
        },
        director,
        genres,
      });
      await queryRunner.commitTransaction();
      return movie;
    } catch (e) {
      await queryRunner.rollbackTransaction();
      throw e;
    } finally {
      await queryRunner.release();
    }
  }

queryRunner.manager.find(Genre, 첫 번째 파라미터에 테이블 정보를 넣어준다.