zoukankan      html  css  js  c++  java
  • 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")
    // 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 只会更新非零字段。 如果您想确保指定字段被更新,你应该使用 Select 更新选定字段,或使用 map 来完成更新操作

    更新选定字段

    如果您想要在更新时选定、忽略某些字段,您可以使用 SelectOmit

    // 使用 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;

    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;

    // 使用 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 支持 BeforeSaveBeforeUpdateAfterSaveAfterUpdate 钩子,这些方法将在更新记录时被调用,详情请参阅 钩子

    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 方法且不追踪更新时间,可以使用 UpdateColumnUpdateColumns,其用法类似于 UpdateUpdates

    // 更新单个列
    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 Data From Modified Rows

    Return changed data, only works for database support Returning, for example:

    // return all columns
    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}}

    // return specified columns
    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}}

    Check Field has changed?

    GORM provides Changed method could be used in Before Update Hooks, it will return the field changed or not

    The Changed method only works with methods UpdateUpdates, and it only checks if the updating value from Update / Updates equals the model value, will return true if it is changed and not omitted

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

    if tx.Statement.Changed("Name", "Admin") { // if Name or Role changed
    tx.Statement.SetColumn("Age", 18)
    }

    // if any fields changed
    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` not changed
    db.Model(&User{ID: 1, Name: "jinzhu"}).Select("Admin").Updates(map[string]interface{
    "name": "jinzhu2", "admin": false,
    })
    // Changed("Name") => false, `Name` not selected to update

    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` not changed
    db.Model(&User{ID: 1, Name: "jinzhu"}).Select("Admin").Updates(User{Name: "jinzhu2"})
    // Changed("Name") => false, `Name` not selected to update

    Change Updating Values

    To change updating values in Before Hooks, you should use SetColumn unless it is a full updates with Save, for example:

    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") {
    s.Age += 20
    tx.Statement.SetColumn("Age", s.Age+20)
    }
    }

    db.Model(&user).Update("Name", "jinzhu")
  • 相关阅读:
    JAVA-初步认识-常用对象API(集合框架-重点方法keyset演示图解)
    JAVA-初步认识-常用对象API(集合框架-常用方法演示)
    JAVA-初步认识-常用对象API(集合框架-Map集合特点常用方法)
    JAVA-初步认识-常用对象API(集合框架-练习-字符串长度排序)
    JAVA-初步认识-常用对象API(集合框架-treeset集合-二叉树)
    JAVA-初步认识-常用对象API(集合框架-treeset集合-Comparator比较器)9
    JAVA-初步认识-常用对象API(集合框架-treeset集合)
    JAVA-初步认识-常用对象API(集合框架-LinkedHashset集合)
    Spring整合hibernate的一个例子:用事务买书
    Spring整合hibernate的一个例子:用事务买书
  • 原文地址:https://www.cnblogs.com/ExMan/p/15668507.html
Copyright © 2011-2022 走看看