CRUD 接口

更新

qianmoQqianmoQ· 更新于 2026-09-23· 阅读 22 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

保存所有字段

传统 API

Save 在执行更新 SQL 时会保存所有字段

db.First(&user)

user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)

Save 是一个 upsert 函数:

  • 如果值中没有主键,则执行 Create
  • 如果值中有主键,则先执行 Update(所有字段,通过 Select(*))。
  • 如果 Update 后 rows affected = 0,则自动回退到 Create。

💡 注意:Save 保证会执行更新或插入操作。
为防止在没有匹配行时意外创建记录,请使用 Select(*).Updates() 。

db.Save(&User{Name: "jinzhu", Age: 100})


db.Save(&User{ID: 1, Name: "jinzhu", Age: 100})

注意 不要将 Save 与 Model 一起使用,这属于未定义行为。

注意 Save 方法已被有意地从泛型 API 中移除,以避免歧义和并发问题。请改用 Create 或 Updates 方法。

更新单个列

使用 Update 更新单个列时,需要提供条件,否则会抛出 ErrMissingWhereClause 错误,详情请查看阻止全局更新。

泛型 API

ctx := context.Background()


err := gorm.G[User](db).Where("active = ?", true).Update(ctx, "name", "hello")



err := gorm.G[User](db).Where("id = ?", 111).Update(ctx, "name", "hello")



err := gorm.G[User](db).Where("id = ? AND active = ?", 111, true).Update(ctx, "name", "hello")

传统 API

当使用 Model 方法且其值包含主键时,将使用主键来构建条件,例如:


db.Model(&User{}).Where("active = ?", true).Update("name", "hello")



db.Model(&user).Update("name", "hello")



db.Model(&user).Where("active = ?", true).Update("name", "hello")

更新多个列

Updates 支持使用 struct 或 map[string]interface{} 进行更新,使用 struct 更新时默认只更新非零字段

泛型 API

ctx := context.Background()


rows, err := gorm.G[User](db).Where("id = ?", 111).Updates(ctx, User{Name: "hello", Age: 18, Active: false})



rows, err := gorm.G[map[string]interface{}](db).Table("users").Where("id = ?", 111).Updates(ctx, map[string]interface{}{"name": "hello", "age": 18, "active": false})

传统 API


db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})



db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})

注意 使用 struct 更新时,GORM 只会更新非零字段。你可能需要使用 map 来更新属性,或使用 Select 指定要更新的字段

更新选定字段

如果你想在更新时只更新选定的字段或忽略某些字段,可以使用 Select、Omit

泛型 API

ctx := context.Background()


rows, err := gorm.G[User](db).Where("id = ?", 111).Select("name").Updates(ctx, map[string]interface{}{"name": "hello", "age": 18, "active": false})


rows, err := gorm.G[User](db).Where("id = ?", 111).Omit("name").Updates(ctx, map[string]interface{}{"name": "hello", "age": 18, "active": false})



rows, err := gorm.G[User](db).Where("id = ?", 111).Select("Name", "Age").Updates(ctx, User{Name: "new_name", Age: 0})



rows, err := gorm.G[User](db).Where("id = ?", 111).Select("*").Updates(ctx, User{Name: "jinzhu", Role: "admin", Age: 0})


rows, err := gorm.G[User](db).Where("id = ?", 111).Select("*").Omit("Role").Updates(ctx, User{Name: "jinzhu", Role: "admin", Age: 0})

传统 API



db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})


db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})



db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})



db.Model(&user).Select("*").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})


db.Model(&user).Select("*").Omit("Role").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})

更新钩子

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 将执行批量更新


db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})



db.Table("users").Where("id IN ?", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})

阻止全局更新

如果你在没有任何条件的情况下执行批量更新,GORM 默认不会执行该操作,而是返回 ErrMissingWhereClause 错误

你必须使用某些条件,或者使用原生 SQL,或者启用 AllowGlobalUpdate 模式,例如:

db.Model(&User{}).Update("name", "jinzhu").Error 

db.Model(&User{}).Where("1 = 1").Update("name", "jinzhu")


db.Exec("UPDATE users SET name = ?", "jinzhu")


db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "jinzhu")

更新记录数

获取更新影响的行数


result := db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})


result.RowsAffected 
result.Error        

高级用法

使用 SQL 表达式更新

GORM 允许使用 SQL 表达式更新列,例如:


db.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))


db.Model(&product).Updates(map[string]interface{}{"price": gorm.Expr("price * ? + ?", 2, 100)})


db.Model(&product).UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))


db.Model(&product).Where("quantity > 1").UpdateColumn("quantity", gorm.Expr("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},
})

使用子查询更新

使用子查询更新表

db.Model(&user).Update("company_name", db.Model(&Company{}).Select("name").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")})

跳过钩子/时间跟踪

如果你想在更新时跳过 Hooks 方法且不跟踪更新时间,可以使用 UpdateColumn、UpdateColumns,它们的行为与 Update、Updates 相同


db.Model(&user).UpdateColumn("name", "hello")



db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18})



db.Model(&user).Select("name", "age").UpdateColumns(User{Name: "hello", Age: 0})

从修改的行返回数据

返回已更改的数据仅适用于支持 Returning 的数据库,例如:


var users []User
db.Model(&users).Clauses(clause.Returning{}).Where("role = ?", "admin").Update("salary", gorm.Expr("salary * ?", 2))




db.Model(&users).Clauses(clause.Returning{Columns: []clause.Column{{Name: "name"}, {Name: "salary"}}}).Where("role = ?", "admin").Update("salary", gorm.Expr("salary * ?", 2))

检查字段是否已更改?

GORM 提供了 Changed 方法,可用于更新前的钩子中,它将返回字段是否已更改。

Changed 方法仅适用于 Update、Updates 方法,且仅检查 Update / Updates 的更新值是否等于模型值。如果值已更改且未被忽略,则返回 true

func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {
  
  if tx.Statement.Changed("Role") {
    return errors.New("role not allowed to change")
  }

  if tx.Statement.Changed("Name", "Admin") { 
    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"})

db.Model(&User{ID: 1, Name: "jinzhu"}).Updates(map[string]interface{"name": "jinzhu"})

db.Model(&User{ID: 1, Name: "jinzhu"}).Select("Admin").Updates(map[string]interface{
  "name": "jinzhu2", "admin": false,
})


db.Model(&User{ID: 1, Name: "jinzhu"}).Updates(User{Name: "jinzhu2"})

db.Model(&User{ID: 1, Name: "jinzhu"}).Updates(User{Name: "jinzhu"})

db.Model(&User{ID: 1, Name: "jinzhu"}).Select("Admin").Updates(User{Name: "jinzhu2"})

修改更新值

要在 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")

GitHub 标签(最新 SemVer)

评论

登录后参与评论

正在加载评论…