Prisma+GQL一对多自关联parentId设为0触发外键错误如何解决
你遇到的报错本质是Prisma的connect关联操作会主动校验关联对象是否存在:当你传入args.parentId=0时,Prisma会先查询是否存在id为0的Post记录,查询失败会直接抛出关联不存在的错误,不会执行后续的数据库写入操作,这和你直接在数据库手动插入数据的场景有本质区别。
按优先级从高到低排列:
方案1:遵循关系型数据库规范,用NULL标识根帖子(最推荐)
关系型数据库中外键字段默认用NULL表示无关联对象,是符合设计标准的实现方式,无需做特殊兼容处理。
首先调整Post模型定义,删除parentId的默认值0配置:model Post { id Int @id @default(autoincrement()) createdAt DateTime @default(now()) description String postedBy User? @relation(fields: [postedById], references: [id]) postedById Int? votes Vote[] comments Post[] @relation(name: "comment") parent Post? @relation(name: "comment", fields: [parentId], references: [id]) parentId Int? }然后调整Mutation逻辑,仅当
parentId为有效值(非0、非空)时才走connect关联:const { userId, prisma } = context; if (userId === null) throw new Error("Not Authenticated"); const createParams = { data: { description: args.description, postedBy: { connect: { id: userId } }, } } // 仅当parentId为有效关联值时添加关联逻辑 if (args.parentId && args.parentId !== 0) { createParams.data.parent = { connect: { id: args.parentId } } } const newPost = await prisma.post.create(createParams); context.pubsub.publish("NEW_POST", newPost); return newPost;后续查询根帖子时直接筛选
parentId: null即可,逻辑和你原本筛选parentId: 0完全一致,改造成本极低。方案2:保留0作为根标识,调整赋值逻辑跳过关联校验
如果你坚持要保留parentId=0作为根帖子的标识,不需要调整模型定义,只需修改Mutation逻辑,不要通过parent.connect传0,直接给parentId字段赋值即可跳过Prisma的关联存在性校验:const { userId, prisma } = context; if (userId === null) throw new Error("Not Authenticated"); const createParams = { data: { description: args.description, postedBy: { connect: { id: userId } }, } } if (args.parentId === 0) { createParams.data.parentId = 0 } else if (args.parentId) { createParams.data.parent = { connect: { id: args.parentId } } } const newPost = await prisma.post.create(createParams); context.pubsub.publish("NEW_POST", newPost); return newPost;该方案可以满足你当前的需求,但不符合关系型数据库设计规范,后续如果开启数据库层面的外键约束会触发写入失败。
方案3:关闭外键约束(不推荐)
如果需要彻底绕过外键校验,可以在Prisma Schema的数据源配置中开启referentialIntegrity = "prisma",由Prisma层管理关联关系,不在数据库层面创建外键约束,该方案会损失数据库层面的关联数据一致性保障,仅适合特殊场景使用。
内容的提问来源于stack exchange,提问作者David Gordon

