Если значение не содержит первичного ключа, выполняется Create
Если у значения есть первичный ключ, сначала выполняется Update (для всех полей с помощью Select(*)).
Если rows affected = 0 после Update, то автоматически выполняется Create.
💡 Примечание: Save гарантирует, что произойдёт либо обновление, либо вставка. Чтобы предотвратить непреднамеренное создание записей при отсутствии совпадений, используйте Select(*).Updates().
db.Save(&User{ID: 1, Name: "jinzhu", Age: 100}) // UPDATE `users` SET `name`="jinzhu",`age`=100,`birthday`="0000-00-00 00:00:00",`update_at`="0000-00-00 00:00:00" WHERE `id` = 1
ПРИМЕЧАНИЕ Не используйте Save с Model, это Неопределенное поведение.
NOTE The Save method is intentionally removed from the Generics API to prevent ambiguity and concurrency issues. Please use Create or Updates methods instead.
Обновление одного столбца
При обновлении одного столбца с помощью Update для него должны быть какие-либо условия, иначе возникнет ошибка ErrMissingWhereClause, проверьте Блокировка глобальных обновлений для получения подробной информации.
Generics API
ctx := context.Background()
// Update with conditions err := gorm.G[User](db).Where("active = ?", true).Update(ctx, "name", "hello") // UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;
// Update with ID condition err := gorm.G[User](db).Where("id = ?", 111).Update(ctx, "name", "hello") // UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
// Update with multiple conditions err := gorm.G[User](db).Where("id = ? AND active = ?", 111, true).Update(ctx, "name", "hello") // UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;
Traditional API
При использовании 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;
// Пользовательский ID равен `111`: db.Model(&user).Update("name", "hello") // UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
// Обновление с условиями и значением модели 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 по умолчанию обновляются только ненулевые поля
Generics API
ctx := context.Background()
// Update attributes with `struct`, will only update non-zero fields err := gorm.G[User](db).Where("id = ?", 111).Updates(ctx, 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;
// Update attributes with `map` err := gorm.G[User](db).Where("id = ?", 111).Updates(ctx, 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;
Traditional API
// Обновление атрибутов с помощью `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
Generics API
ctx := context.Background()
// Select with Map err := gorm.G[User](db).Where("id = ?", 111).Select("name").Updates(ctx, map[string]interface{}{"name": "hello", "age": 18, "active": false}) // UPDATE users SET name='hello' WHERE id=111;
// Select with Struct (select zero value fields) err := gorm.G[User](db).Where("id = ?", 111).Select("Name", "Age").Updates(ctx, 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) err := gorm.G[User](db).Where("id = ?", 111).Select("*").Updates(ctx, User{Name: "jinzhu", Role: "admin", Age: 0})
// Select all fields but omit Role (select all fields include zero value fields) err := gorm.G[User](db).Where("id = ?", 111).Select("*").Omit("Role").Updates(ctx, User{Name: "jinzhu", Role: "admin", Age: 0})
Traditional API
// Выбрать c Map // Пользовательский ID равен `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;
// Выбрать со структурой (выберите поля с нулевым значением) db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0}) // UPDATE users SET name='new_name', age=0 WHERE id=111;
// Выбрать все поля (выбрать все поля, включающие поля с нулевым значением) 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})
Хуки Update
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 }
Updates пакетами
Если мы не указали запись, имеющую значение первичного ключа с помощью Model, GORM выполнит пакетное обновление
// Обновление со структурой 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);
Глобальные блокировки при Updates
Если вы выполните пакетное обновление без каких-либо условий, GORM НЕ запустит его и вернет ошибку ErrMissingWhereClause по умолчанию
Вы должны использовать некоторые условия, или использовать необработанный SQL, или включить режим AllowGlobalUpdate, например:
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';
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
Update с помощью подзапроса
Обновление таблицы при помощи подзапроса
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")})
Без использования хуков/отслеживания времени
Если вы хотите пропустить хуки и не отслеживать время обновления при обновлении, вы можете использовать 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;
Возврат данных из измененных строк
Возврат измененных данных работает только для баз данных, которые поддерживают возврат, например:
// Возвратить все колонки 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}}
GORM предоставляет метод Changed, который можно было бы использовать в Before Updates Hooks, он сообщит, изменилось поле или нет.
Метод 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()) } returnnil }
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` не выбрано для обновления