gorm 更新——迹忆客-ag捕鱼王app官网
保存所有字段
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
,查看 阻止全局更新 了解详情。 当使用 model 方法,并且值中有主键值时,主键将会被用于构建条件,例如:
// 条件更新
db.model(&user{}).where("active = ?", true).update("name", "hello")
// 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;
// 根据 `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 只会更新非零值的字段。 你可以使用map
更新字段,或者使用 select 指定要更新的字段
更新选定字段
如果我们想要在更新时选定、忽略某些字段,我们可以使用 select、omit
// select with map
// 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;
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;
// select with struct (select zero value fields)
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 all fields (select all fields include zero value fields)
db.model(&user).select("*").updates(user{name: "jinzhu", role: "admin", age: 0})
// select all fields but omit role (select all fields include zero value fields)
db.model(&user).select("*").omit("role").updates(user{name: "jinzhu", role: "admin", age: 0})
更新 hook
gorm 支持的 hook 点包括:beforesave, beforeupdate, aftersave, afterupdate. 更新记录时将调用这些方法,查看 hooks 获取详细信息
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';
// 根据 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 // 更新的错误
高级选项
使用 sql 表达式更新
gorm 允许使用 sql 表达式更新列,例如:
// product 的 id 是 `3`
db.model(&product).update("price", gorm.expr("price * ? ?", 2, 100))
// update "products" set "price" = price * 2 100, "updated_at" = '2013-11-17 21:34:10' where "id" = 3;
db.model(&product).updates(map[string]interface{}{"price": gorm.expr("price * ? ?", 2, 100)})
// update "products" set "price" = price * 2 100, "updated_at" = '2013-11-17 21:34:10' where "id" = 3;
db.model(&product).updatecolumn("quantity", gorm.expr("quantity - ?", 1))
// update "products" set "quantity" = quantity - 1 where "id" = 3;
db.model(&product).where("quantity > 1").updatecolumn("quantity", gorm.expr("quantity - ?", 1))
// update "products" set "quantity" = quantity - 1 where "id" = 3 and quantity > 1;
并且 gorm 也允许使用 sql 表达式、自定义数据类型的 context valuer 来更新,例如:
// 根据自定义数据类型创建
type location struct {
x, y int
}
func (loc location) gormvalue(ctx context.context, db *gorm.db) clause.expr {
return clause.expr{
sql: "st_pointfromtext(?)",
vars: []interface{}{fmt.sprintf("point(%d %d)", loc.x, loc.y)},
}
}
db.model(&user{id: 1}).updates(user{
name: "jinzhu",
location: location{x: 100, y: 100},
})
// update `user_with_points` set `name`="jinzhu",`location`=st_pointfromtext("point(100 100)") where `id` = 1
根据子查询进行更新
使用子查询更新表
db.model(&user).update("company_name", db.model(&company{}).select("name").where("companies.id = users.company_id"))
// update "users" set "company_name" = (select name from companies where companies.id = users.company_id);
db.table("users as u").where("name = ?", "jinzhu").update("company_name", db.table("companies as c").select("name").where("c.id = u.company_id"))
db.table("users as u").where("name = ?", "jinzhu").updates(map[string]interface{}{"company_name": db.table("companies as c").select("name").where("c.id = u.company_id")})
不使用 hook 和时间追踪
如果我们想在更新时跳过 hook 方法且不追踪更新时间,可以使用 updatecolumn
、updatecolumns
,其用法类似于 update、updates
// 更新单个列
db.model(&user).updatecolumn("name", "hello")
// update users set name='hello' where id = 111;
// 更新多个列
db.model(&user).updatecolumns(user{name: "hello", age: 18})
// update users set name='hello', age=18 where id = 111;
// 更新选中的列
db.model(&user).select("name", "age").updatecolumns(user{name: "hello", age: 0})
// update users set name='hello', age=0 where id = 111;
返回修改行的数据
返回被修改的数据,仅适用于支持 returning 的数据库,例如:
// 返回所有列
var users []user
db.model(&users).clauses(clause.returning{}).where("role = ?", "admin").update("salary", gorm.expr("salary * ?", 2))
// update `users` set `salary`=salary * 2,`updated_at`="2021-10-28 17:37:23.19" where role = "admin" returning *
// users => []user{{id: 1, name: "jinzhu", role: "admin", salary: 100}, {id: 2, name: "jinzhu.2", role: "admin", salary: 1000}}
// 返回指定的列
db.model(&users).clauses(clause.returning{columns: []clause.column{{name: "name"}, {name: "salary"}}}).where("role = ?", "admin").update("salary", gorm.expr("salary * ?", 2))
// update `users` set `salary`=salary * 2,`updated_at`="2021-10-28 17:37:23.19" where role = "admin" returning `name`, `salary`
// users => []user{{id: 0, name: "jinzhu", role: "", salary: 100}, {id: 0, name: "jinzhu.2", role: "", salary: 1000}}
检查字段是否有变更?
gorm 提供了 changed 方法,它可以被用在 before update hook 里,它会返回字段是否有变更的布尔值.
changed 方法只能与 update、updates 方法一起使用,并且它只是检查 model 对象字段的值与 update、updates 的值是否相等。 如果值有变更,且字段没有被忽略,则返回 true.
func (u *user) beforeupdate(tx *gorm.db) (err error) {
// 如果 role 字段有变更
if tx.statement.changed("role") {
return errors.new("role not allowed to change")
}
if tx.statement.changed("name", "admin") { // 如果 name 或 role 字段有变更
tx.statement.setcolumn("age", 18)
}
// 如果任意字段有变更
if tx.statement.changed() {
tx.statement.setcolumn("refreshedat", time.now())
}
return nil
}
db.model(&user{id: 1, name: "jinzhu"}).updates(map[string]interface{"name": "jinzhu2"})
// changed("name") => true
db.model(&user{id: 1, name: "jinzhu"}).updates(map[string]interface{"name": "jinzhu"})
// changed("name") => false, 因为 `name` 没有变更
db.model(&user{id: 1, name: "jinzhu"}).select("admin").updates(map[string]interface{
"name": "jinzhu2", "admin": false,
})
// changed("name") => false, 因为 `name` 没有被 select 选中并更新
db.model(&user{id: 1, name: "jinzhu"}).updates(user{name: "jinzhu2"})
// changed("name") => true
db.model(&user{id: 1, name: "jinzhu"}).updates(user{name: "jinzhu"})
// changed("name") => false, 因为 `name` 没有变更
db.model(&user{id: 1, name: "jinzhu"}).select("admin").updates(user{name: "jinzhu2"})
// changed("name") => false, 因为 `name` 没有被 select 选中并更新
在 update 时修改值
若要在 before 钩子中改变要更新的值,如果它是一个完整的更新,可以使用 save;否则,应该使用 setcolumn ,例如:
func (user *user) beforesave(tx *gorm.db) (err error) {
if pw, err := bcrypt.generatefrompassword(user.password, 0); err == nil {
tx.statement.setcolumn("encryptedpassword", pw)
}
if tx.statement.changed("code") {
user.age = 20
tx.statement.setcolumn("age", user.age)
}
}
db.model(&user).update("name", "jinzhu")