如何在GORM中仅当关联不存在时创建关联?
问题:GORM多对多关联下替换FirstOrCreate后重复创建标签
我需要遍历字符串数组,仅在标签不存在时创建对应记录,再创建关联这些标签的文章。原本用FirstOrCreate能正常工作,但换成First()+判断ErrRecordNotFound的方式后,即便数据库里已有对应slug的标签,每次执行都会生成新ID的标签。
原有代码(正常工作的FirstOrCreate版本)
遍历标签处理代码
var postTags []models.Tag for _, tagSlug := range tagsArray { tag := models.Tag{ Slug: tagSlug, } err = dbi.Where("slug = ?", tagSlug).FirstOrCreate(&tag).Error if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Internal Server Error", }) } postTags = append(postTags, tag) }
创建文章代码
post := models.Post{ // 其他字段赋值... Tags: postTags } dbi.Create(&post)
模型定义
type Post struct { BaseModel Title string `json:"title"` MarkdownUploadURL string `json:"markdownUploadUrl"` AuthorID string `json:"authorId"` Tags []Tag `json:"tags" gorm:"many2many:posts_tags"` } type Tag struct { BaseModel Slug string `json:"slug"` }
问题重现
把dbi.FirstOrCreate()替换为dbi.First(),并通过errors.Is(err, gorm.ErrRecordNotFound)判断是否创建新标签后,数据库中已存在的标签仍会被重复创建,生成新ID。
解决方案
问题核心是手动创建的Tag实例没有被正确赋值已存在标签的ID,导致GORM将其识别为新记录。正确逻辑如下:
var postTags []models.Tag for _, tagSlug := range tagsArray { var tag models.Tag // 先查询对应slug的标签 err = dbi.Where("slug = ?", tagSlug).First(&tag).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { // 标签不存在,创建新标签 tag = models.Tag{Slug: tagSlug} if err := dbi.Create(&tag).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Internal Server Error", }) } } else { // 处理其他查询错误 return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Internal Server Error", }) } } // 将带正确ID的标签加入关联数组 postTags = append(postTags, tag) }
原因说明
FirstOrCreate会自动将查询到的已有标签数据赋值给传入的变量,确保变量包含数据库中已存在的ID,GORM在关联时会识别这是已有记录,不会重复创建。- 你之前的错误写法中,查询失败后可能重新创建了新的
Tag实例但未复用查询变量,或者查询到的标签没有正确传递到后续逻辑,导致GORM因为实例无ID而判定为新记录,最终重复插入。
内容的提问来源于stack exchange,提问作者gabefgonc
相关产品推荐
相关产品推荐

