为了账号安全,请及时绑定邮箱和手机立即绑定

Updates() 似乎没有更新关联

Updates() 似乎没有更新关联

Go
白衣染霜花 2022-08-01 16:32:24
我有以下结构。type Relation struct {    gorm.Model    OwnerID      uint          `json:"ownerID"`    OwnerType    string        `json:"ownerType"`    Addresses    []Address     `gorm:"polymorphic:Owner;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"addresses"`}type Company struct {    gorm.Model    Name     string   `json:"name"`    Relation Relation `gorm:"polymorphic:Owner;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"relation"`}type Address struct {    gorm.Model    OwnerID   uint   `json:"ownerID"`    OwnerType string `json:"ownerType"`    Country   string `json:"country"`    Zip       string `json:"zip"`    Number    uint   `json:"number,string"`    Addition  string `json:"addition"`    Street    string `json:"street"`    State     string `json:"state"`    City      string `json:"city"`}以及以下处理程序func UpdateCompany(db *database.Database) fiber.Handler {    return func(c *fiber.Ctx) error {        id, err := IDFromParams(c)        if err != nil {            return c.JSON(responseKit.ParameterMissing())        }        update := new(model.Company)        if err = json.Unmarshal(c.Body(), update); err != nil {            return c.JSON(responseKit.ParsingError())        }        if result := db.Session(&gorm.Session{FullSaveAssociations: true}).Where("id = ?", id).Debug().Updates(update); result.Error != nil {            return c.JSON(responseKit.RecordUpdateError())        }        updated := new(model.Company)        result := db.Preload("Relation.Addresses").            Find(updated, id)        if result.Error != nil {            return c.JSON(responseKit.RecordReadError())        }        return c.JSON(responseKit.RecordUpdatedSuccess(updated))    }}我读了https://gorm.io/docs/associations.html和https://github.com/go-gorm/gorm/issues/3487#issuecomment-698303344但它似乎不起作用。我使用 rest 客户端使用以下 JSON 调用终结点{    "name": "yep",    "relation": {        "adresses": [            {                "number": "5"            }        ]    }}
查看完整描述

1 回答

?
慕的地8271018

TA贡献1796条经验 获得超4个赞

您看到的 Insert 语句实际上是 Upsert 语句,您会注意到在插入部分之后,它们具有 ON CONFLICT 子句。如果主键已经在表中(即存在键冲突,因此得名),则这样做就是更新记录。按预期工作也是如此。Updates

但!

还有一个问题。就目前而言,这些语句仍会导致将新关系和新地址插入到数据库中,因为不会有主键冲突。这是因为您尚未为“关系”或“地址”行输入主键(您将看到插入中没有列)。id

经验法则:

Gorm 将主键为零的结构视为新行,并在保存时插入它们。推论:您只能更新具有非零主键的结构。

您可能认为您在调用中提供了主键,但这仅适用于顶级结构。当 gorm 处理关系时,它看不到任何主键,并假定您要添加新关系,而不是更新现有关系。Where

如果你考虑一下,这是有道理的:如果你不提供 Relation.ID,gorm怎么知道哪个关系目前与公司有关。知道这一点的方法是首先使用OwnerID和OwnerType进行选择以找出答案,但这不是gorm会为您做的事情(gorm通常试图做到极简主义,并且不会尝试自己找到比您提供的信息更多的信息)。

解决此问题的一种简单方法是让您的API用户在输入中提供 Relation.ID 和Address.ID,但在这种情况下,这是非常不方便的。

我发现解决此问题的一个很好的模式是首先加载目标根对象的当前状态以及要更新的相关关系,然后将来自API用户的更改应用于结构和关系,然后使用。这将保存所有字段,无论它们是否已更改。Save

另一种选择是将关系的现有 ID 放入您从 DB 中提取的版本,然后像您在此处所做的那样调用。update.Relation.IDupdate.Relation.Addresses[i].IDUpdates

奖励:HasMany有更多的更新挑战

对于“地址”,需要特别注意,因为它是一个 HasMany 关系。当调用gorm永远不会删除关系时,例如,当您之前有3个地址并且现在只想拥有两个地址时,它只会更新其中两个地址,而第三个地址则悬而未决;这是为了防止错误地删除数据。相反,你必须明确你的意图。Save/Updates

然后,您要做的是使用关联模式来替换关联,但是在调用 :Updates

tx := db.Begin()

// do your thing

err := tx.

    Session(&gorm.Session{FullSaveAssociations: true}).

    Where("id = ?", id).

    Omit("Relation.Addresses")

    Updates(update).

    Error

if err != nil {

    tx.Rollback()

    // handle error

}

// assuming update.Relation.ID is set

err = tx.

    Model(&update.Relation).

    Association("Addresses").

    Replace(&update.Relation.Addresses)

if err != nil {

    tx.Rollback()

    // handle error

}

if err := tx.Commit().Error; err !=  nil {

    tx.Rollback()

    // handle error

}


查看完整回答
反对 回复 2022-08-01
  • 1 回答
  • 0 关注
  • 120 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信