问题描述
原有数据结构,如下:
type AdvConfirm struct {
ID int `gorm:"column:id;primary_key" json:"id"`
StaffID int `gorm:"column:staff_id" json:"staff_id"`
AdvertiserID int `gorm:"column:advertiser_id" json:"advertiser_id"`
ProductID int `gorm:"column:product_id" json:"product_id"`
IsConfirmed int `gorm:"column:is_confirmed" json:"is_confirmed"`
Charge float64 `gorm:"column:charge" json:"charge"`
DiscountCharge float64 `gorm:"column:discount_charge" json:"discount_charge"`
ActivationCharge float64 `gorm:"column:activation_charge" json:"activation_charge"`
RebateCharge float64 `gorm:"column:rebate_charge" json:"rebate_charge"`
CleaningCharge float64 `gorm:"column:cleaning_charge" json:"cleaning_charge"`
ChargeMonth string `gorm:"column:charge_month" json:"charge_month"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
}
现在,通过如下方式更新数据库:
func (ac *AdvConfirm) TableName() string {
return "adv_confirm"
}
func (ac *AdvConfirm) UpdateOperatorStatus() error {
db, err := GetGormDB()
if err != nil {
return err
}
var m AdvConfirm
if acc.ID == 0 { // 插入
err = db.Create(ac).Error
} else {
err = db.Debug().Model(&m).Updates(ac).Error
}
if err != nil {
return err
}
return nil
}
发现,当更改IsConfirmed字段的值从0改为1时,正常更新;当更新IsConfirmed字段的值从1改到0时,数据库中该字段的值还是1,该字段没有正常更新,但是其他非零值字段都更新为想要的值了。
这就有点纳闷了,通过查阅gorm官方文档,发现通过Struct的方式更新字段时,对于零值不会被更新。如果想正常更新零值,应该使用map的方式进行更新操作。
提示: 通过结构体变量更新字段值, gorm库会忽略零值字段。就是字段值等于0, nil, “”, false这些值会被忽略掉,不会更新。如果想更新零值,可以使用map类型替代结构体。
通过如下map的形式更新,即可正常更新零值的情况:
func (ac *AdvConfirm) UpdateOperatorStatus() error {
db, err := GetGormDB()
if err != nil {
return err
}
if ac.ID == 0 { // 插入
err = db.Create(ac).Error
} else {
err = db.Debug().Model(acc).Updates(map[string]interface{}{"id": ac.ID, "staff_id": ac.StaffID, "advertiser_id": ac.AdvertiserID, "product_id": ac.ProductID, "is_confirmed": ac.IsConfirmed, "charge": ac.Charge, "discount_charge": ac.DiscountCharge, "activation_charge": ac.ActivationCharge, "rebate_charge": ac.RebateCharge, "cleaning_charge": ac.CleaningCharge, "charge_month": ac.ChargeMonth}).Error
}
if err != nil {
return err
}
return nil
}
gorm更新方式
保存所有字段
Save 会保存所有的字段,即使字段是零值
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
// UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;
更新单个列
当使用 Update 更新单个列时,你需要指定条件,否则会返回 ErrMissingWhereClause 错误,查看 Block Global Updates 获取详情。当使用了 Model 方法,且该对象主键有值,该值会被用于构建条件,例如:
// 条件更新
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
// 等价于更新SQL语句:UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;
// User 的 ID 是 `111`
db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
// 根据条件和 model 的值进行更新
db.Model(&user).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;
更新多列
Updates 方法支持 struct 和 map[string]interface{} 参数。当使用 struct 更新时,默认情况下,GORM 只会更新非零值的字段
根据 struct
更新属性,只会更新非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;由于Active字段是bool类型的零值false,这里使用结构体更新,不会更新该字段的零值
根据 map
更新所有属性,包括零值字段
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
注意 当通过 struct 更新时,GORM 只会更新非零字段。 如果想确保指定字段被更新,应该使用 Select 更新选定字段,或使用 map 来完成所有属性(包括零值字段)更新操作。
更新选定字段
如果想要在更新时,只指定或忽略某些字段进行更新,可以使用 Select、Omit方法。
// 使用 Map 进行 Select
// User's ID is `111`:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello' WHERE id=111;虽然map中包括多个字段,但是更新时调用Select方法只指定name字段更新
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;这里使用Omit指定更新时要忽略的字段,即对该字段不更新
// 使用 Struct 进行 Select(会 select 零值的字段)
db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
// UPDATE users SET name='new_name', age=0 WHERE id=111;
// Select 所有字段(查询包括零值字段的所有字段)
db.Model(&user).Select("*").Update(User{Name: "jinzhu", Role: "admin", Age: 0})
// Select 除 Role 外的所有字段(包括零值字段的所有字段)
db.Model(&user).Select("*").Omit("Role").Update(User{Name: "jinzhu", Role: "admin", Age: 0})
更新 Hook
对于更新操作,GORM 支持 BeforeSave、BeforeUpdate、AfterSave、AfterUpdate 钩子,这些方法将在更新记录时被调用。
func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {
if u.Role == "admin" {
return errors.New("admin user not allowed to update")
}
return
}
批量更新
如果没有通过 Model 指定记录的主键,则 GORM 会执行批量更新
// 根据 struct 更新
db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18 WHERE role = 'admin; 更新所有role为admin的记录
// 根据 map 更新
db.Table("users").Where("id IN ?", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
// UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);
阻止全局更新
如果在没有任何条件的情况下执行批量更新,默认情况下,GORM 不会执行该操作,并返回 ErrMissingWhereClause 错误
对此,你必须加一些条件,或者使用原生 SQL,或者启用 AllowGlobalUpdate 模式,例如:
db.Model(&User{}).Update("name", "jinzhu").Error // gorm.ErrMissingWhereClause
db.Model(&User{}).Where("1 = 1").Update("name", "jinzhu")
// UPDATE users SET `name` = "jinzhu" WHERE 1=1
db.Exec("UPDATE users SET name = ?", "jinzhu")
// UPDATE users SET name = "jinzhu"
db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "jinzhu")
// UPDATE users SET `name` = "jinzhu"
更新的记录数
可以通过 RowsAffected
获取受更新影响的行数。
result := db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18 WHERE role = 'admin;
result.RowsAffected // 更新的记录数
result.Error // 更新的错误
总结
通过结构体变量更新字段值, gorm库会忽略零值字段。即:通过update struct的方式更新值时,当字段值等于0, nil, “”, false这些值会被忽略掉,这些值不会更新。如果想更新零值,可以使用updates map类型替代结构体。