NestJS+TypeORM(MySQL)使用orIgnore报实体ID未设置错误
问题背景
使用NestJS + TypeORM(MySQL)实现Insert Ignore功能时,调用QueryBuilder的orIgnore()方法触发报错:Cannot update entity because entity id is not set in the entity.。直接在MySQL Workbench执行生成的SQL语句正常,手动在values中设置id: null也能运行,但需要根本解决方案。
根本解决方案
方案1:明确指定插入列(排除id)
在QueryBuilder中通过.columns()指定要插入的字段,不包含自增主键id,让MySQL自动处理主键生成:
await this.userSearchRepository .createQueryBuilder() .insert() .into(User_SEARCH) .columns(['userId', 'etc1']) // 仅指定需要插入的列 .values({ userId: user.id, etc1: user.name }) .orIgnore() .execute();
原因:原SQL中TypeORM自动包含了id列并设为DEFAULT,但实体类中id的类型为非可选的number,导致内部逻辑误判为需要更新而非插入。排除id列后,TypeORM不再处理该字段,避免触发错误检查。
方案2:使用Repository.save()配合ignoreDuplicates选项
放弃QueryBuilder,直接用Repository的save()方法并开启ignoreDuplicates,TypeORM会自动生成INSERT IGNORE语句:
await this.userSearchRepository.save( { userId: user.id, etc1: user.name }, { ignoreDuplicates: true } );
原因:该方法内部会正确处理自增主键的生成逻辑,同时忽略重复数据,无需手动干预字段设置。
方案3:调整实体类id字段类型为可选
修改实体类中id字段的类型为number | undefined,允许其未定义:
@Entity() export class User_SEARCH { @PrimaryGeneratedColumn() @IsNumber() id: number | undefined; // 修改为可选类型 // 其他字段保持不变 @IsNumber() @Column({ nullable: true }) userId: number; @IsString() @Column() etc1: string; @IsString() @Column({nullable:true}) etc2: string; @ManyToOne(() => User, (user) => user.userSearch, { onDelete: 'CASCADE' }) @JoinColumn({ referencedColumnName: 'id', name: 'userId', }) user: User; }
原因:实体类中id原本为必填的number类型,当未传入时TypeORM会认为字段缺失,触发错误检查。改为可选类型后,符合自增字段无需手动赋值的逻辑。
验证说明
以上三种方案均能避免手动设置id: null的临时处理,从根源解决TypeORM内部的字段校验逻辑问题,同时保证INSERT IGNORE功能正常运行。
内容的提问来源于stack exchange,提问作者user10874312

