如何使用GORM创建或更新记录?

如何使用GORM创建或更新记录?,go,orm,go-gorm,Go,Orm,Go Gorm,Gorm有一个FirstOrCreate方法和一个FirstOrInit,但是之后如何检查记录是否确实创建了?我喜欢创建一个不存在的记录,如果它存在,我想更新一些字段。下面是gorm文档中的示例 见ATTR。它不会确切地告诉您是否确实创建了记录,但只允许您在记录确实创建时更新某些字段(这似乎是您最终想要实现的) 如果存在,那么 UPDATE "aggregated_data" SET "data" = '[{"a":2}]', "type" = '2' WHERE "aggregated_d

Gorm有一个
FirstOrCreate
方法和一个
FirstOrInit
,但是之后如何检查记录是否确实创建了?我喜欢创建一个不存在的记录,如果它存在,我想更新一些字段。

下面是gorm文档中的示例

见ATTR。它不会确切地告诉您是否确实创建了记录,但只允许您在记录确实创建时更新某些字段(这似乎是您最终想要实现的)

如果存在,那么

 UPDATE "aggregated_data" SET "data" = '[{"a":2}]', "type" = '2'  WHERE "aggregated_data"."id" = '2' AND (("aggregated_data"."type" = '2'))  
否则

更新2020.10.09

谢谢你

从1.20.x开始,GORM为不同的数据库提供兼容的Upsert支持()

//将列更新为'id'上的新值冲突
DB.条款{
列:[]子句。列{{Name:“id”},//键列
doUpdate:clause.AssignmentColumns([]字符串{“name”,“age”}),//需要更新列
}).创建(&用户)
//在不匹配时使用***合并到“用户”,然后在匹配时插入***,然后更新集“名称”=“排除”。“名称”;SQL Server
//在“用户”***中插入冲突(“id”)是否更新设置“名称”=“已排除”。“名称”,“年龄”=“已排除”。“年龄”;PostgreSQL
//在重复密钥更新时将`name`=值(名称),`age=值(年龄)插入`users`***;MySQL
对于gorm 1.9.x或更低版本,首先更新,然后在不存在时插入更有效

// update only set name=nick
if err := db.Model(&newUser).Where("id = ?", 3333).Update("name", "nick").Error; err != nil {
    // always handle error like this, cause errors maybe happened when connection failed or something. 
    // record not found...
    if gorm.IsRecordNotFoundError(err){
        db.Create(&newUser)  // create new record from newUser
    }
}

FirstOrInit
FirstOrCreate
是不同的。如果数据库中没有匹配记录,
FirstOrInit
将初始化结构,但不创建记录,
FirstOrCreate
将创建一条记录,并向结构查询该记录。

投票最多的答案对我不起作用,但这确实:

user := NewUser(email, password)
if db.Model(&user).Where("email = ?", email).Updates(&user).RowsAffected == 0 {
    db.Create(&user)
}

这适用于gorm v1.9.15和go 1.13,有更好的方法:

func CreateOrUpdate(db *gorm.DB, model interface{}, where interface{}, update interface{}) (interface{}, error) {
    var result interface{}
    err := db.Model(model).Where(where).First(result).Error
    if err != nil {
        if !errors.Is(err, gorm.ErrRecordNotFound) {
            return nil, err
        } else {
            //insert
            if err = db.Model(model).Create(update).Error; err != nil {
                return nil, err
            }
        }
    }
    //not update some field
    reflect.ValueOf(update).Elem().FieldByName("someField").SetInt(0)
    if err = db.Model(model).Where(where).Updates(update).Error; err != nil {
        return nil, err
    }
    return update, nil
}
    if err := db.Where(User{Email: "some@email.com"}).
       Assign(User{Email: "some@email.com", Age: 45}).
       FirstOrCreate(&User{}).Error; err != nil {
         c.Next(err)
         return
    }
在本例中,如果用户使用电子邮件“some@email.com,则“年龄”字段将被更新。相反,如果找不到用户,则创建它

请注意,我将丢弃创建的用户,但如果需要,您可以保留引用。
此外,出于某些GORM原因,要求在Assign子句中至少提供一个筛选器字段,这就是为什么您会看到电子邮件被填充两次。

正确的链接在这里:(无法编辑,因为它少于6个字符)将嵌入链接从curd更改为crud这对我来说没有意义-为什么需要指定where子句来更新现有记录。如果用户对象是现有记录,那么它应该已经有了id,我希望gorm足够聪明,知道如果id存在,它应该更新。我为Java编写了一个类似gorm的库,它只需要调用save(),而不管记录是否为新记录-如果记录没有id,它将插入记录,如果记录有id,它将进行更新。问题是“如果它存在,我想更新一些字段”。因此,您必须检查它是否存在,如果存在,您只能更新所选字段。如果您使用
db.save(&user)
,则用户世界中的所有字段都将写入db。Gorm现在拥有对upsert的一流支持
user := NewUser(email, password)
if db.Model(&user).Where("email = ?", email).Updates(&user).RowsAffected == 0 {
    db.Create(&user)
}
func CreateOrUpdate(db *gorm.DB, model interface{}, where interface{}, update interface{}) (interface{}, error) {
    var result interface{}
    err := db.Model(model).Where(where).First(result).Error
    if err != nil {
        if !errors.Is(err, gorm.ErrRecordNotFound) {
            return nil, err
        } else {
            //insert
            if err = db.Model(model).Create(update).Error; err != nil {
                return nil, err
            }
        }
    }
    //not update some field
    reflect.ValueOf(update).Elem().FieldByName("someField").SetInt(0)
    if err = db.Model(model).Where(where).Updates(update).Error; err != nil {
        return nil, err
    }
    return update, nil
}
    if err := db.Where(User{Email: "some@email.com"}).
       Assign(User{Email: "some@email.com", Age: 45}).
       FirstOrCreate(&User{}).Error; err != nil {
         c.Next(err)
         return
    }