[NestJS] - 14. TypeORM JOIN과 QueryBuilder
이전 포스팅에서 TypeORM을 이용해 관련 테이블들을 외래키 연결 및 생성하는 코드를 작성해보았다.
이번에는 SQL의 Join에 해당하는 TypeORM 질의를 작성해보려고 한다.
Relations
const result = await this.productRepository.find({
relations: ['productCategory', 'productTags'],
});
단순하게 Left Join하여 모든 컬럼들을 가져오려면 relations를 작성해주면 된다.
인수로는 Join하려는 모든 릴레이션 테이블들을 리스트로 작성한다.
SELECT
`Product`.`id` AS `Product_id`,
`Product`.`name` AS `Product_name`,
`Product`.`description` AS `Product_description`,
`Product`.`price` AS `Product_price`,
`Product`.`isSoldOut` AS `Product_isSoldOut`,
`Product`.`deletedAt` AS `Product_deletedAt`,
`Product`.`updatedAt` AS `Product_updatedAt`,
`Product`.`productSalesLocationId` AS `Product_productSalesLocationId`,
`Product`.`productCategoryId` AS `Product_productCategoryId`,
`Product`.`userId` AS `Product_userId`,
`Product__productCategory`.`id` AS `Product__productCategory_id`,
`Product__productCategory`.`name` AS `Product__productCategory_name`,
`Product__productTags`.`id` AS `Product__productTags_id`,
`Product__productTags`.`name` AS `Product__productTags_name`
FROM `product` `Product`
LEFT JOIN `product_category` `Product__productCategory`
ON `Product__productCategory`.`id`=`Product`.`productCategoryId`
LEFT JOIN `product_product_tags_product_tag` `Product_Product__productTags`
ON `Product_Product__productTags`.`productId`=`Product`.`id`
LEFT JOIN `product_tag` `Product__productTags`
ON `Product__productTags`.`id`=`Product_Product__productTags`.`productTagId`
WHERE `Product`.`deletedAt` IS NULL
다음의 쿼리와 동일하다.
Join
async findAll(): Promise<Product[]> {
const result = await this.productRepository.find({
join: {
alias: 'p',
leftJoinAndSelect: {
id: 'p.productCategory'
},
},
});
return result;
}
다음과 같이 Alias를 지정하여 Join Select 할 수도 있다.
QueryBuilder
위처럼 Join하여 사용하는 방법을 TypeORM에서 제공하기는 하지만 Select 시 모든 컬럼을 선택해야 하는 점, Multiple한 테이블을 Join하기 까다로운 점 등이 문제가 있다.
따라서 TypeORM에서 QueryBuilder라는 기능을 제공한다.
제공하는 기능이 많으며 웬만한 SQL문을 작성할 수 있으므로 Select하는 경우 QueryBuilder를 이용하는 것이 좋다.
QueryBuilder : JoinAndSelect
const result = await this.productRepository
.createQueryBuilder('p')
.leftJoinAndSelect('p.productCategory', 'pc')
.leftJoinAndSelect('p.productTags', 'pt')
.where('p.id = :id and pc.name = pt.name', { id: id })
.getOne();
- createQueryBuilder()를 통해 쿼리를 생성하고 Alias를 지정한다.
- leftJoinAndSelect은 Join하면서 대상 테이블의 모든 컬럼을 Select한다. Join할 Key 컬럼과 Alias를 지정한다.
- where문에는 조건을 입력하고, 파라미터는 :param 의 형태로 입력할 수 있다.
- getOne(), getMany()등을 이용해서 결과를 얻어온다.
SELECT `p`.`id` AS `p_id`, `p`.`name` AS `p_name`,
`p`.`description` AS `p_description`,
`p`.`price` AS `p_price`,
`p`.`isSoldOut` AS `p_isSoldOut`,
`p`.`deletedAt` AS `p_deletedAt`,
`p`.`updatedAt` AS `p_updatedAt`,
`p`.`productSalesLocationId` AS `p_productSalesLocationId`,
`p`.`productCategoryId` AS `p_productCategoryId`,
`p`.`userId` AS `p_userId`,
`pc`.`id` AS `pc_id`,
`pc`.`name` AS `pc_name`,
`pt`.`id` AS `pt_id`,
`pt`.`name` AS `pt_name`
FROM `product` `p`
LEFT JOIN `product_category` `pc`
ON `pc`.`id`=`p`.`productCategoryId`
LEFT JOIN `product_product_tags_product_tag` `p_pt`
ON `p_pt`.`productId`=`p`.`id`
LEFT JOIN `product_tag` `pt`
ON `pt`.`id`=`p_pt`.`productTagId`
WHERE ( `p`.`id` = ? and `pc`.`name` = `pt`.`name` )
AND ( `p`.`deletedAt` IS NULL ) -- PARAMETERS: ["18"]
다음과 같은 쿼리가 실행된다.
QueryBuilder : Join
const result = await this.productRepository
.createQueryBuilder('p')
.select(['p.id', 'p.name', 'p.price', 'pc.name', 'pt.name'])
.leftJoin('p.productCategory', 'pc')
.leftJoin('p.productTags', 'pt')
.where('p.id = :id and pc.name = pt.name', { id: id })
.getOne();
- createQueryBuilder()를 통해 쿼리를 생성하고 Alias를 지정한다.
- leftJoin은 Join하면서 대상 테이블을 Select하지 않는다. Join할 컬럼과 Alias를 지정한다.
- select()에 Select할 컬럼들을 선택적으로 작성할 수 있다.
SELECT `p`.`id` AS `p_id`,
`p`.`name` AS `p_name`,
`p`.`price` AS `p_price`,
`pc`.`name` AS `pc_name`,
`pc`.`id` AS `pc_id`,
`pt`.`name` AS `pt_name`,
`pt`.`id` AS `pt_id`
FROM `product` `p`
LEFT JOIN `product_category` `pc`
ON `pc`.`id`=`p`.`productCategoryId`
LEFT JOIN `product_product_tags_product_tag` `p_pt`
ON `p_pt`.`productId`=`p`.`id`
LEFT JOIN `product_tag` `pt`
ON `pt`.`id`=`p_pt`.`productTagId`
WHERE ( `p`.`id` = ? and `pc`.`name` = `pt`.`name` )
AND ( `p`.`deletedAt` IS NULL ) -- PARAMETERS: ["18"]
다음과 같이 모든 컬럼을 선택하지 않고 원하는 컬럼만을 선택할 수 있다.
QueryBuilder에 대한 기능들은 공식 문서를 참고하면 언급한 내용 외에 많은 Example을 제공하고 있다.
https://typeorm.io/select-query-builder#joining-relations
해당 강의를 들으면서 학습한 내용을 바탕으로 저만의 프로젝트를 만드는 과정을 기록하여 남기는 것을 목표로 하고 있습니다.
주관적인 생각이 들어가 있을 수 있으므로 혹시 틀린 내용이 있다면 피드백 부탁드립니다.
'Backend > Node.js (NestJS)' 카테고리의 다른 글
[NestJS] - 16. TypeORM View와 Raw Query, Index/Unique 제약조건 걸기 (0) | 2023.02.25 |
---|---|
[NestJS] - 15. TypeORM 트랜잭션(Transaction) (0) | 2023.02.25 |
[NestJS] - 13. TypeORM 관계 테이블 Insert/Update (1) | 2023.02.23 |
[NestJS] - 12. TypeORM Select (0) | 2023.02.23 |
[NestJS] - 11. TypeORM Delete, 실제 삭제와 소프트 삭제 (1) | 2023.02.23 |