NestJS+TypeORM多对多关系如何单次请求新增Item并关联多个已有分类
问题背景
我有两张表,分别为Items和Categories,二者为多对多关系,即一个Item可归属多个分类,一个分类下也可包含多个Item。
示意图:
当前使用的技术栈为NestJS搭配TypeORM。
遇到的问题
需要在单次请求中完成Item入库,同时为其绑定多个所属分类。分类数据已提前完成数据库初始化且数量固定,无需同步新增分类数据。
现有临时实现
当前方案是先通过createItemDto中传入的categoryID查询到对应的分类实体,再在createItem方法中将新增的Item实例添加到分类实体的items数组中完成关联,现有代码如下:
items.service.ts中的CreateItem方法
async createItem( createItemDto: CreateItemDto, user: User, category: Category, ): Promise<Item> { const newItem = await this.itemsRepository.save({ name: createItemDto.name, description: createItemDto.description, price: createItemDto.price, delivery: createItemDto.delivery, cancellation: createItemDto.cancellation, rating: createItemDto.rating, imageUrl: createItemDto.imageUrl, }); user.items = [...user.items, newItem]; await user.save(); category.items = [...category.items, newItem]; await category.save(); return newItem; }
items.controller.ts
@Post('/createitem') async createItem(@Body() createItemDto: CreateItemDto): Promise<Item> { const user = await this.authService.getUserById(createItemDto.userId); const category = await this.categoriesService.getCategoryById( createItemDto.categoryId, ); return this.itemsService.createItem(createItemDto, user, category); }
Item实体
import { Category } from './category.entity'; import { Rent } from './rent.entity'; import { User } from './user.entity'; import { BaseEntity, Column, Entity, JoinTable, ManyToMany, ManyToOne, OneToOne, PrimaryGeneratedColumn, } from 'typeorm'; @Entity('item') export class Item extends BaseEntity { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() description: string; @Column() price: number; @Column() delivery: boolean; @Column() cancellation: boolean; @Column({ nullable: true }) rating: number; @Column() imageUrl: string; @ManyToOne(() => User, (user) => user.items, { onDelete: 'CASCADE', }) user: User; @OneToOne(() => Rent, (rent) => rent.item) rent: Rent; @ManyToMany(() => Category, (category) => category.items) @JoinTable() categories: Category[]; }
Category实体
import { Item } from './item.entity'; import { BaseEntity, Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn, } from 'typeorm'; @Entity() export class Category extends BaseEntity { @PrimaryGeneratedColumn() id: number; @Column() category: string; @ManyToMany(() => Item, (item) => item.categories) items: Item[]; }
Categories服务
import { Category } from './../entities/category.entity'; import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { CategoriesRepository } from './categories.repository'; @Injectable() export class CategoriesService { constructor( @InjectRepository(CategoriesRepository) private categoriesRepository: CategoriesRepository, ) {} async getCategoryById(id: number): Promise<Category> { return await this.categoriesRepository.findOne(id, { relations: ['items'], }); } }
解决方案
可直接利用TypeORM的关联自动维护能力,单次操作完成Item入库和多分类绑定,无需手动更新关联实体的属性,性能更高,具体修改如下:
步骤1:调整CreateItemDto,支持多分类ID传入
将Dto中原本的单值categoryId字段修改为数组类型categoryIds: number[],适配多分类绑定需求。
步骤2:修改Controller逻辑,批量查询分类
@Post('/createitem') async createItem(@Body() createItemDto: CreateItemDto): Promise<Item> { const user = await this.authService.getUserById(createItemDto.userId); // 批量查询所有需要关联的分类实体 const categories = await Promise.all( createItemDto.categoryIds.map(id => this.categoriesService.getCategoryById(id)) ); return this.itemsService.createItem(createItemDto, user, categories); }
步骤3:修改Service的createItem方法,直接关联数据
async createItem( createItemDto: CreateItemDto, user: User, categories: Category[], ): Promise<Item> { // 保存Item时直接赋值关联属性,TypeORM会自动维护中间表数据 const newItem = await this.itemsRepository.save({ name: createItemDto.name, description: createItemDto.description, price: createItemDto.price, delivery: createItemDto.delivery, cancellation: createItemDto.cancellation, rating: createItemDto.rating, imageUrl: createItemDto.imageUrl, user: user, categories: categories }); return newItem; }
可选优化
分类查询不需要携带items关联字段,仅需要分类主键即可完成关联,可修改getCategoryById方法减少不必要的查询开销:
async getCategoryById(id: number): Promise<Category> { return await this.categoriesRepository.findOne(id); }
内容的提问来源于stack exchange,提问作者Marko Marchisio
相关产品推荐
相关产品推荐

