如何使用 GORM 创建或更新记录?
How to Create or Update a record with GORM?
Gorm 有一个 FirstOrCreate
方法和一个 FirstOrInit
但如何检查记录是否实际创建?我喜欢创建一条记录,如果它不存在,如果它存在,我想更新一些字段。
这是来自 gorm 文档的示例 CRUD section
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
db.NewRecord(user) // => returns `true` as primary key is blank
db.Create(&user)
db.NewRecord(user) // => return `false` after `user` created
参见属性 here。它不会确切地告诉你记录是否真的被创建了,但是只有当记录被实际创建时才会让你更新一些字段(这似乎是你最终想要实现的)。
gormDB.Where(entity.AggregatedData{Type: v.Type}).Assign(entity.AggregatedData{Type: v.Type, Data: v.Data}).FirstOrCreate(v)
SELECT * FROM "aggregated_data" WHERE ("aggregated_data"."type" = '2') ORDER BY "aggregated_data"."id" ASC LIMIT 1
如果存在则
UPDATE "aggregated_data" SET "data" = '[{"a":2}]', "type" = '2' WHERE "aggregated_data"."id" = '2' AND (("aggregated_data"."type" = '2'))
其他
INSERT INTO "aggregated_data" ("data","type") VALUES ('[{"a":2}]','1') RETURNING "aggregated_data"."id"
更新2020.10.09
感谢@vaelin
从1.20.x开始,GORM为不同的数据库提供兼容的Upsert支持(Upsert-On-Conflict)
// Update columns to new value on `id` conflict
DB.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}}, // key colume
DoUpdates: clause.AssignmentColumns([]string{"name", "age"}), // column needed to be updated
}).Create(&users)
// MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET "name"="excluded"."name"; SQL Server
// INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age"; PostgreSQL
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `name`=VALUES(name),`age=VALUES(age); 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 子句中至少提供一个筛选字段,这就是为什么您会看到 email 被填充两次。
FirstOrInit
没有创建新记录。它只找到第一个匹配的记录,如果找不到则用给定的条件初始化它。
对于 FirstOrCreate
和 FirstOrInit
,您可以使用 RowsAffected
。如果 return 值为“1”,则在数据库中找到该记录,即它已经存在,因此未创建。如果 return 值为“0”,则未找到。
... if it exists I want to update some fields.
我不确定你想在哪里更新。在您的 map
/struct
本地或数据库中。如果是本地的,那么我相信您现在可以做到。如果在数据库中,我建议使用 Attrs
or Assign
方法。
Gorm 有一个 FirstOrCreate
方法和一个 FirstOrInit
但如何检查记录是否实际创建?我喜欢创建一条记录,如果它不存在,如果它存在,我想更新一些字段。
这是来自 gorm 文档的示例 CRUD section
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
db.NewRecord(user) // => returns `true` as primary key is blank
db.Create(&user)
db.NewRecord(user) // => return `false` after `user` created
参见属性 here。它不会确切地告诉你记录是否真的被创建了,但是只有当记录被实际创建时才会让你更新一些字段(这似乎是你最终想要实现的)。
gormDB.Where(entity.AggregatedData{Type: v.Type}).Assign(entity.AggregatedData{Type: v.Type, Data: v.Data}).FirstOrCreate(v)
SELECT * FROM "aggregated_data" WHERE ("aggregated_data"."type" = '2') ORDER BY "aggregated_data"."id" ASC LIMIT 1
如果存在则
UPDATE "aggregated_data" SET "data" = '[{"a":2}]', "type" = '2' WHERE "aggregated_data"."id" = '2' AND (("aggregated_data"."type" = '2'))
其他
INSERT INTO "aggregated_data" ("data","type") VALUES ('[{"a":2}]','1') RETURNING "aggregated_data"."id"
更新2020.10.09
感谢@vaelin
从1.20.x开始,GORM为不同的数据库提供兼容的Upsert支持(Upsert-On-Conflict)
// Update columns to new value on `id` conflict
DB.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}}, // key colume
DoUpdates: clause.AssignmentColumns([]string{"name", "age"}), // column needed to be updated
}).Create(&users)
// MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET "name"="excluded"."name"; SQL Server
// INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age"; PostgreSQL
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `name`=VALUES(name),`age=VALUES(age); 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 子句中至少提供一个筛选字段,这就是为什么您会看到 email 被填充两次。
FirstOrInit
没有创建新记录。它只找到第一个匹配的记录,如果找不到则用给定的条件初始化它。
对于 FirstOrCreate
和 FirstOrInit
,您可以使用 RowsAffected
。如果 return 值为“1”,则在数据库中找到该记录,即它已经存在,因此未创建。如果 return 值为“0”,则未找到。
... if it exists I want to update some fields.
我不确定你想在哪里更新。在您的 map
/struct
本地或数据库中。如果是本地的,那么我相信您现在可以做到。如果在数据库中,我建议使用 Attrs
or Assign
方法。