TypeORM中1:N关联下advertiserId无法插入关联列的问题排查
解决TypeORM中关联字段advertiserId无法插入的问题
问题描述
已配置Advertiser与AdvertisementRequest表的1:N关联实体,但无法将advertiserId插入到advertisement-request表的advertiserId列。在服务层代码中执行advertisementRequest.advertiserId = decodeJwt.advertiserId赋值后保存,该字段仍为undefined或null。
AdvertisementRequest 实体
import { Exclude } from 'class-transformer'; import {Column ,CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, ManyToOne, RelationId} from 'typeorm'; import {Advertiser} from "./advertiser.entity"; @Entity('advertisement_request') export class AdvertisementRequest { @PrimaryGeneratedColumn() id: number; @Column() companyName: string; @Column() companyRegisterNumber: string; @Column() productName: string; @Column() creatorSubscriberRangeId: number; @Column() creatorHeadcount: number; @CreateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP(6)'}) createdAt: Date; @UpdateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP(6)', onUpdate: 'CURRENT_TIMESTAMP(6)'}) updatedAt: Date; @ManyToOne(() => Advertiser) advertiser: Advertiser @RelationId((advertisementRequest : AdvertisementRequest) => advertisementRequest.advertiser) advertiserId: number;}
Advertiser 实体
import { Exclude } from 'class-transformer'; import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, OneToMany } from 'typeorm'; import {AdvertisementRequest} from "./advertisement-request.entity"; @Entity('advertiser') export class Advertiser { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() email: string; @Exclude() @Column({ type: 'varchar', length: 255 }) password: string; @CreateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP(6)' }) createdAt: Date; @UpdateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP(6)', onUpdate: 'CURRENT_TIMESTAMP(6)' }) updatedAt: Date; @OneToMany(() => AdvertisementRequest, advertisementRequest => advertisementRequest.advertiser) advertisementRequests: AdvertisementRequest[]; }
AdvertisementRequestService 代码
import { Injectable } from '@nestjs/common'; import { AdvertiseRequestDto } from "./dto/advertise-request.dto"; import { AdvertisementRequest } from "../common/entities/advertisement-request.entity"; import { AdvertisementRequestRepository } from "./advertisement-request.repository"; import { AdvertiserRepository } from "../advertiser/advertiser.repository"; @Injectable() export class AdvertisementRequestService { constructor( private readonly AdvertisementRequestRepository: AdvertisementRequestRepository, private readonly advertiserRepository: AdvertiserRepository ) {} async advertiseRequest( advertisementData: AdvertiseRequestDto, decodeJwt: any ): Promise<AdvertisementRequest>{ const { companyName, companyRegisterNumber, productName, creatorSubscriberRangeId, creatorHeadcount } = advertisementData; const advertisementRequest = new AdvertisementRequest(); advertisementRequest.companyName = companyName; advertisementRequest.companyRegisterNumber = companyRegisterNumber; advertisementRequest.productName = productName; advertisementRequest.creatorSubscriberRangeId = creatorSubscriberRangeId; advertisementRequest.creatorHeadcount = creatorHeadcount; advertisementRequest.advertiserId = decodeJwt.advertiserId; // 无法插入到数据库列 const saveAdvertisementRequest = await this.AdvertisementRequestRepository.save(advertisementRequest); return saveAdvertisementRequest; } }
解决方案
问题根源在于:TypeORM的@RelationId装饰器生成的是只读字段,它仅用于从关联的advertiser实体中自动提取ID,无法通过直接赋值的方式将其持久化到数据库。以下两种方法可以解决这个问题:
方法一:关联Advertiser实体对象
通过查询获取对应的Advertiser实例,将实例赋值给advertisementRequest的advertiser属性,TypeORM会自动处理外键字段的插入:
修改服务层代码:
async advertiseRequest( advertisementData: AdvertiseRequestDto, decodeJwt: any ): Promise<AdvertisementRequest>{ const { companyName, companyRegisterNumber, productName, creatorSubscriberRangeId, creatorHeadcount } = advertisementData; // 根据ID查询对应的Advertiser实体 const advertiser = await this.advertiserRepository.findOneBy({ id: decodeJwt.advertiserId }); // 可选:如果找不到广告主,抛出异常 if (!advertiser) { throw new NotFoundException('广告主不存在'); } const advertisementRequest = new AdvertisementRequest(); advertisementRequest.companyName = companyName; advertisementRequest.companyRegisterNumber = companyRegisterNumber; advertisementRequest.productName = productName; advertisementRequest.creatorSubscriberRangeId = creatorSubscriberRangeId; advertisementRequest.creatorHeadcount = creatorHeadcount; // 关联实体对象,替代直接赋值advertiserId advertisementRequest.advertiser = advertiser; const saveAdvertisementRequest = await this.AdvertisementRequestRepository.save(advertisementRequest); return saveAdvertisementRequest; }
方法二:手动添加独立的advertiserId列
如果不想每次都查询Advertiser实体,可以修改AdvertisementRequest实体,添加一个带@Column的独立advertiserId字段,同时保留关联关系:
修改AdvertisementRequest实体:
import { Exclude } from 'class-transformer'; import {Column ,CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, ManyToOne, JoinColumn} from 'typeorm'; import {Advertiser} from "./advertiser.entity"; @Entity('advertisement_request') export class AdvertisementRequest { @PrimaryGeneratedColumn() id: number; @Column() companyName: string; @Column() companyRegisterNumber: string; @Column() productName: string; @Column() creatorSubscriberRangeId: number; @Column() creatorHeadcount: number; @CreateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP(6)'}) createdAt: Date; @UpdateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP(6)', onUpdate: 'CURRENT_TIMESTAMP(6)'}) updatedAt: Date; // 手动添加外键列 @Column() advertiserId: number; // 配置关联关系,指定关联的列名 @ManyToOne(() => Advertiser, { nullable: false }) @JoinColumn({ name: 'advertiserId' }) advertiser: Advertiser; }
修改后,服务层中直接赋值advertisementRequest.advertiserId = decodeJwt.advertiserId即可正常将值插入数据库,同时TypeORM仍会维护实体间的关联关系。
内容的提问来源于stack exchange,提问作者Hwanseog Choi
相关产品推荐
相关产品推荐

