保存所有字段
Save
會保存所有的字段,即使字段是零值、
db.First(&user, 5)
user.Name = sql.NullString{"王八", true}
user.Age = 9
db.Debug().Save(&user)
// UPDATE `users` SET `name`='王八',`age`=9,`uuid`='c3987500-bf01-412b-aaab-685285e04037',`created_at`='2021-11-17 13:34:30' WHERE `id` = 5
更新單個列
當使用 Update
更新單個列時,你需要指定條件,否則會返回 ErrMissingWhereClause
錯誤,查看 Block Global Updates 獲取詳情。當使用了 Model
方法,且該對象主鍵有值,該值會被用於構建條件,例如:
db.Debug().Model(&User{}).Where("age = ?", 12).Update("name", "張無忌")
// UPDATE `users` SET `name`='張無忌' WHERE age = 12
var user User
db.First(&user, "age = ?", 9)
db.Debug().Model(&user).Update("name", "龜")
// UPDATE `users` SET `name`='龜' WHERE `id` = 5 // 由於user對象主鍵有值,所以就會被用於構建條件
更新多列
Updates
方法支持 struct
和 map[string]interface{}
參數。當使用 struct
更新時,默認情況下,GORM 只會更新非零值的字段
// 根據struct更新屬性,只會更新非零值字段
var user User
db.First(&user, 7)
db.Debug().Model(&user).Updates(User{Name: sql.NullString{"哈哈2", true}, Age: 0})
// UPDATE `users` SET `name`='哈哈2' WHERE `id` = 7 // 結構體只會更新非零值字段
// 根據map更新屬性,零值和非零值字段都會更新
var user User
db.Debug().First(&user, 7).Updates(map[string]interface{}{
"name": "", "age": 18,
})
// SELECT * FROM `users` WHERE `users`.`id` = 7 ORDER BY `users`.`id` LIMIT 1
// UPDATE `users` SET `age`=18,`name`='' WHERE `users`.`id` = 7 AND `id` = 7 ORDER BY `users`.`id` LIMIT 1
注意 當通過 struct 更新時,GORM 只會更新非零字段。 如果您想確保指定字段被更新,你應該使用 Select
更新選定字段,或使用 map
來完成更新操作
更新選定字段
如果您想要在更新時選定、忽略某些字段,您可以使用 Select
、Omit
// select+map更新
db.Debug().Model(new(User)).Where("id = ?", 7).Select("name").Updates(map[string]interface{}{
"uuid": "def214f8-4b39-11ec-a337-fc3fdbb57b40",
"created_at": time.Now(),
"name": "哈哈哈",
})
// UPDATE `users` SET `name`='哈哈哈' WHERE id = 7
// Omit + map更新
db.Debug().Model(new(User)).Where("id = ?", 7).Omit("name").Updates(map[string]interface{}{
"uuid": "def214f8-4b39-11ec-a337-fc3fdbb57b40",
"created_at": time.Now(),
"name": "哈哈哈",
})
// UPDATE `users` SET `created_at`='2021-11-22 10:31:52.602',`uuid`='def214f8-4b39-11ec-a337-fc3fdbb57b40' WHERE id = 7
// select + struct更新, 會更新零值的字段
db.Debug().Model(&User{}).Where(map[string]interface{}{"id": 7}).Select("name", "age").Updates(User{
Name: sql.NullString{"struct2", true},
Age: 0,
})
// UPDATE `users` SET `name`='struct2',`age`=0 WHERE `id` = 7
// Omit + struct更新, 不會更新零值的字段
db.Debug().Model(&User{}).Where(map[string]interface{}{"id": 7}).Omit("name").Updates(User{
Name: sql.NullString{"struct2", true},
Age: 0,
})
// 此語句不執行
// select 所有字段,(更新包括零值字段的所有字段)
db.Debug().Model(new(User)).Where("id = ?", 7).Select("*").Updates(User{
Name: sql.NullString{"", true},
Age: 34,
CreatedAt: time.Now(),
})
// UPDATE `users` SET `id`=0,`name`='',`age`=34,`uuid`='00000000-0000-0000-0000-000000000000',`created_at`='2021-11-22 10:45:21.161' WHERE id = 7
// Omit 更新除name字段的所有字段,(包括零值字段的所有字段)
db.Debug().Model(new(User)).Where("id = ?", 0).Omit("name").Updates(User{
Name: sql.NullString{"", true},
Age: 44,
CreatedAt: time.Now(),
ID: 7,
})
// UPDATE `users` SET `id`=7,`age`=44,`created_at`='2021-11-22 10:51:23.298' WHERE id = 0
更新 Hook
對於更新操作,GORM 支持 BeforeSave
、BeforeUpdate
、AfterSave
、AfterUpdate
鈎子,這些方法將在更新記錄時被調用
func (u *User) BeforeUpdate(*gorm.DB) (err error) { fmt.Println("User BeforeUpdate執行了") return } func (u *User) AfterUpdate(*gorm.DB) (err error) { fmt.Println("User AfterUpdate執行了") return } func (u *User) BeforeSave(*gorm.DB) (err error) { fmt.Println("User BeforeSave執行了") return } func (u *User) AfterSave(*gorm.DB) (err error) { fmt.Println("User AfterSave執行了") return }
// 更新Hook BeforeSave、AfterSave、BeforeUpdate、AfterUpdate
// 鈎子方法的函數簽名是:func(*gorm.DB) error
spew.Println("更新前")
db.Debug().Model(new(User)).Where("id = ?", 7).Updates(User{
Name: sql.NullString{"hello", true},
Age: 36,
})
spew.Println("更新后")
/* 打印結果
更新前
User BeforeSave執行了
User BeforeUpdate執行了
User AfterSave執行了
User AfterUpdate執行了
2021/11/22 11:26:26 C:/Users/mayanan/Desktop/pro_go/topgoer_gorm/main.go:114 SLOW SQL >= 200ms
[260.657ms] [rows:1] UPDATE `users` SET `name`='hello',`age`=36 WHERE id = 7
更新后
*/
批量更新
如果您尚未通過 Model
指定記錄的主鍵,則 GORM 會執行批量更新
// 根據struct批量更新
db.Debug().Model(new(User)).Where("name <> ?", "").Updates(User{
Name: sql.NullString{"mayanan", true},
Age: 28,
})
// UPDATE `users` SET `name`='mayanan',`age`=28 WHERE name <> ''
// 根據map批量更新
db.Debug().Model(new(User)).Where("name <> ?", "").Updates(map[string]interface{}{
"name": "王五", "age": 39,
})
// UPDATE `users` SET `age`=39,`name`='王五' WHERE name <> ''
阻止全局更新
如果在沒有任何條件的情況下執行批量更新,默認情況下,GORM 不會執行該操作,並返回 ErrMissingWhereClause
錯誤
對此,你必須加一些條件,或者使用原生 SQL,或者啟用 AllowGlobalUpdate
模式,例如:
// gorm 默認阻止全局更新
db.Debug().Model(&User{}).Updates(map[string]interface{}{
"name": "lisi", "age": 33,
})
// 報錯:WHERE conditions required
db.Debug().Model(&User{}).Where("1 = 1").Updates(map[string]interface{}{
"name": "lisi", "age": 33,
})
// UPDATE `users` SET `age`=33,`name`='lisi' WHERE 1 = 1
db.Debug().Exec("update users set name = ?", "田七")
// update users set name = '田七';
db.Debug().Session(&gorm.Session{AllowGlobalUpdate: true}).Updates(User{
Age: 35,
})
// UPDATE `users` SET `age`=33
更新的記錄數
獲取受更新影響的行數
// 獲取受更新影響的行數
ret := db.Debug().Session(&gorm.Session{AllowGlobalUpdate: true}).Updates(User{
Age: 37,
})
spew.Dump(ret.RowsAffected) // 5 更新的記錄數
spew.Dump(ret.Error) // nil 更新產生的錯誤
高級選項
使用 SQL 表達式更新
GORM 允許使用 SQL 表達式更新列,例如:
var user User
db.First(&user)
db.Debug().Model(&user).Update("age", gorm.Expr("age * ? + ?", 2, 3))
// UPDATE `users` SET `age`=age * 2 + 3 WHERE `id` = 1
// map里面使用sql表達式
var user []User
db.Find(&user)
db.Debug().Model(&user).Updates(map[string]interface{}{
"age": gorm.Expr("(age - ?) / ?", 2, 2),
})
// UPDATE `users` SET `age`=(age - 2) / 2 WHERE (`id` = 1 OR `id` = 2 OR `id` = 5 OR `id` = 6 OR `id` = 7)
// UpdateColumn里面使用sql表達式
var user []User
db.Find(&user)
db.Debug().Model(&user).UpdateColumn("age", gorm.Expr("age / ? - ?", 2, 2))
// UPDATE `users` SET `age`=age / 2 - 2 WHERE (`id` = 1 OR `id` = 2 OR `id` = 5 OR `id` = 6 OR `id` = 7)
根據子查詢進行更新
使用子查詢更新表
// 根據子查詢更新表
var user User
db.First(&user, 6)
subQuery := db.Model(new(Email)).Select("user_id").Where("email = ?", "abc@qq.com")
db.Debug().Model(&user).Update("age", subQuery)
// UPDATE `users` SET `age`=(SELECT `user_id` FROM `emails` WHERE email = 'abc@qq.com') WHERE `id` = 6
subQuery := db.Table("emails as e").Select("email").Where("user_id = ?", 10)
db.Debug().Table("users as u").Where("id = ?", 7).Update("name", subQuery)
// UPDATE users as u SET `name`=(SELECT email FROM emails as e WHERE e.user_id = 11) WHERE id = 5
subQuery := db.Table("emails as e").Select("email").Where("user_id = ?", 2)
db.Debug().Table("users as u").Where("id = ?", 2).Updates(map[string]interface{}{
"name": subQuery,
})
// UPDATE users as u SET `name`=(SELECT email FROM emails as e WHERE user_id = 2) WHERE id = 2
不使用 Hook 和時間追蹤
如果您想在更新時跳過 Hook
方法且不追蹤更新時間,可以使用 UpdateColumn
、UpdateColumns
,其用法類似於 Update
、Updates
// 更新單個列
db.Debug().Table("users").Where("id = ?", 1).UpdateColumn("name", "李四")
// UPDATE `users` SET `name`='李四' WHERE id = 1
// 更新多個列
db.Debug().Table("users").Where("id = ?", 2).UpdateColumns(map[string]interface{}{
"name": "王五", "age": 31,
})
// UPDATE `users` SET `age`=31,`name`='王五' WHERE id = 2
db.Debug().Table("users").Where("id = ?", 2).Select("name").UpdateColumns(User{
Name: sql.NullString{"王十六", true}, Age: 36,
})
// UPDATE `users` SET `name`='王十六' WHERE id = 2