[typeORM] leftJoin, condition.. query는 어떻게 쓸까?

손연주·2022년 5월 13일
0

들어가기 전

leftJoin 또는 테이블 join에 대한 이해가 부족하다면 SQL query문 연습 - leftjoin를 먼저 보고 오면 좋을 것 같다.

TypeORM - Query Builder 해당 문서를 읽으며 작성하였습니다.

Parameters

"student.id = :id", { id: 1 }

Here,
:id - parameter name
{ id: 1 } - value of the parameter

student.id를 id로 칭하고 다음 파라미터를 객체로 받으며 그 안에서 찾을 값을 부여해 id값으로 해당 데이터를 찾는다. where문과 함께 쓰며 확인해 보자.

Adding expression

where

where문은 조건에 맞는 데이터들을 filter해준다.

createQueryBuilder("student").where("student.id = :id", { id: 1 })

이는 다음과 같다.

select * from student where student.id=1;

Join

두 개 또는 그 이상, 서로 다른 테이블을 참조하고 있는 컬럼을 참고하여 합치는 것이다.

import {Entity, PrimaryGeneratedColumn, Column, OneToMany} from "typeorm"; 
import {Project} from "./Project"; 

@Entity() 
export class Student {
   
   @PrimaryGeneratedColumn() 
   id: number; 
   
   @Column() 
   name: string; 
   
   @OneToMany(type => Project, project => project.student) 
   projects: project[]; 
}
import {Entity, PrimaryGeneratedColumn, Column, ManyToOne} from "typeorm"; 
import {Student} from "./Student"; 

@Entity() 
export class Project { 

   @PrimaryGeneratedColumn() 
   id: number; 
   
   @Column() 
   title: string; 
   
   @ManyToOne(type => Student, student => student.projects) 
   student: Student; 
}

@OneToMany, @ManyToOne

사실 나에겐 원투매니와 매니투원을 쉽게 외우는 방법이 있다. @OneToMany를 Student는 One 하나고, to Many 많은 것으로 간다. 즉, 학생은 많은 걸 가질 수 있다 => 학생은 많은 프로젝트를 가질 수 있다 && @ManyToOne Project는 Many 많지만, ToOne 하나에게 간다. 즉, 프로젝트 수는 많지만 하나의 student만 가질 수 있다. 라는 생각의 흐름.. 부끄럽지만 누군가에게는 도움이 될까 남긴다🙄.

다시 코드로 돌아와서

student는 project라는 컬럼을 가지고 있고 그 값 하나하나들은 Project entity의 PK인 id을 참조하고 있다.

Student1라는 이름을 가진 학생의 정보와 그 학생이 참여한 프로젝트의 정보를 모두 가져오려면 어떻게 해야 될까?

위에 서술한 정보만으로 쿼리문을 작성해보겠다.

.createQueryBuilder("student")
.select('project.title', 'title)
.leftJoin('project를 기준으로 student랑 leftjoin하고 싶다')
.where("student.name = :name", { name: 'Student1' })
.getOne();

문서 내용 >

.createQueryBuilder("student") 
.leftJoinAndSelect("student.projects", "project") 
.where("student.name = :name", { name: "Student1" }) 
.getOne();
SELECT student.*, project.* FROM students student 
LEFT JOIN projects project ON project.student = student.id 
WHERE student.name = 'Student1'

select문을 따로 쓰지 않고 leftJoinAndSelect문을 이용했다.

leftJoin 사용법

leftJoin('기준이 되는 테이블.연결할 엔티티', '연결할 엔티티')

Join without selection

.createQueryBuilder("student") .innerJoin("student.projects", "project") 
.where("student.name = :name", { name: "student1" }) 
.getOne();

The above query is equivalent to −

SELECT student.* FROM students student 
INNER JOIN projects project ON project.student = student.id 
WHERE student.name = 'Student1';

select없이 join을 할 수도 있는데 그렇게 되면 테이블만 조인을 한 상태가 되고 값을 가져오진 않는다. 따라서 join을 해준 뒤 select문을 따로 써서 가져올 컬럼을 명시해주자. 한 엔티티의 전체 칼럼 데이터가 필요하다면 joinAndSelect문을 쓴다.

실전 예시

DB 설명

User(작업자) 엔티티와 Profit(작업자의 정산값을 가지고 있다) 엔티티를 Join하여 user의 name, email, userId 그리고 profit의 workerProfit, reviewerProfit을 가지고 오려고 한다.

프로젝트 단위로 정산값이 생겨서 Profit에는 user의 PK와 subtaskId(프로젝트id)를 가지고 있고, 하나의 subtask(프로젝트)는 여러명의 user를 가지고 있다. 해당 프로젝트에 참여한 전체 인원의 정보가 필요한 것이므로 subtaskId를 파라미터로 받아 해당 subtask의 전체 userId를 추출한 뒤 그 값으로 user와 profit을 join해준다.

code

async findBySubtaskIdForExcel(
    params: ExcelProfitDto,
  ): Promise<findBySubtaskIdForExcelReturn[]> {
    const { subtaskId } = params;

    if (!subtaskId) {
      throw new InvalidPropertyException('isString', { property: subtaskId });
    }

    const userIdObject = await this.profitRepository
      .createQueryBuilder('profit')
      .select('profit.userId', 'userId')
      .where('profit.subtaskId = :subtaskId', { subtaskId })
      .getRawMany();

    const userIds = userIdObject.map((user) => user.userId);

    const profitData = await this.profitRepository
      .createQueryBuilder('profit')
      .select('profit.subtaskId', 'subtaskId')
      .addSelect('profit.userId', 'userId')
      .addSelect('profit.workerProfit', 'workerProfit')
      .addSelect('profit.reviewerProfit', 'reviewerProfit')
      .addSelect('user.name', 'name')
      .addSelect('user.email', 'email')
      .leftJoin('profit.user', 'user')
      .andWhere('profit.userId IN (:...userIds)', { userIds })
      .where('profit.subtaskId = :subtaskId', { subtaskId })
      .getRawMany();

    return profitData;
  }

마무리하며, 시퀄라이즈만 사용했던 터라 익숙한 듯 낯선 typeORM을 마주했을 때 조금 두렵기도 했지만 역시 정답은 공식문서에 있다는 걸 깨달았다. 어렵거나 이해가 안되면 하나하나 찾아서 알아가면 된다. 그럼 언젠간 퍼즐이 맞춰진다.

profile
할 수 있다는 생각이 정말 나를 할 수 있게 만들어준다.

2개의 댓글

comment-user-thumbnail
2022년 5월 13일

완투매니, 매니투완,,,, 잘 봤읍니다,,,,
@>------------

1개의 답글