如何通过单请求创建Coupon与Discount实体并建立关联?(遇报错)
一对多关联实体(Coupon与Discount)批量创建问题排查与解决
场景说明
我有Coupon和Discount两个实体,二者为一对多关系:一个Coupon可对应多个Discount,一个Discount仅属于一个Coupon。需要通过单个请求同时创建新的Coupon和Discount,请求体格式如下:
{ "title": "someTitle", "description": "someDesc", "deadline": "someDeadline", "image": "someImg", "rules": "someRules", "company":"15", "category":"1", "discounts": [ { "discount":"ad", "price":"awd" } ] }
请求完成后,需根据请求体中的discounts数组创建Discount实体,同时创建Coupon实体并完成两者关联。
相关实体与服务代码
coupon.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm'; import { Discount } from './discount.entity'; @Entity() export class Coupon { @PrimaryGeneratedColumn() id: number; @Column() title: string; @Column() description: string; @Column() deadline: string; @Column() image: string; @Column() rules: string; @Column() company: number; @Column() category: number; @OneToMany(() => Discount, discount => discount.coupon, { cascade: true }) discounts: Discount[]; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; }
discount.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, CreateDateColumn, UpdateDateColumn } from 'typeorm'; import { Coupon } from './coupon.entity'; @Entity() export class Discount { @PrimaryGeneratedColumn() id: number; @Column() discount: string; @Column() price: string; @ManyToOne(() => Coupon, coupon => coupon.discounts) coupon: Coupon; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; }
coupon.service.ts
import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Coupon } from './coupon.entity'; import { Discount } from './discount.entity'; @Injectable() export class CouponService { constructor( @InjectRepository(Coupon) private couponRepository: Repository<Coupon>, @InjectRepository(Discount) private discountRepository: Repository<Discount>, ) {} async create(couponData: any) { const { discounts, ...couponFields } = couponData; const coupon = this.couponRepository.create(couponFields); await this.couponRepository.save(coupon); const discountEntities = discounts.map(discount => { const disc = this.discountRepository.create(discount); disc.coupon = coupon; return disc; }); await this.discountRepository.save(discountEntities); return coupon; } }
报错信息
当前执行操作后出现报错:
TypeError: Cannot set property 'coupon' of undefined
问题解决
1. 优化级联保存逻辑
Coupon实体的@OneToMany已经配置了{ cascade: true },无需单独保存Discount,直接通过Coupon的级联保存即可完成关联创建,避免手动关联时的实例异常:
async create(couponData: any) { // 校验discounts数组合法性 if (!couponData.discounts || !Array.isArray(couponData.discounts)) { throw new Error('discounts必须为非空数组'); } // 将请求体中的discounts转为Discount实体实例 const discountEntities = couponData.discounts.map(discount => this.discountRepository.create(discount) ); // 创建Coupon并挂载关联的Discount实例 const coupon = this.couponRepository.create({ ...couponData, discounts: discountEntities }); // 仅保存Coupon,TypeORM自动级联创建Discount并完成关联 return await this.couponRepository.save(coupon); }
2. 显式添加外键字段(可选)
在Discount实体中添加显式外键字段,避免实体引用的潜在问题:
@Column() couponId: number; @ManyToOne(() => Coupon, coupon => coupon.discounts) coupon: Coupon;
此时关联时可直接设置外键ID:
const disc = this.discountRepository.create(discount); disc.couponId = coupon.id;
3. 增加请求体校验
在Controller层添加DTO校验,确保请求体中的discounts数组结构合法,避免空值或非数组传入导致的map异常。
内容的提问来源于stack exchange,提问作者Sargis Tadevosyan
相关产品推荐
相关产品推荐

