更新与删除
2026/1/15大约 5 分钟GORMUpdateDelete
更新与删除
一、更新记录
1.1 Save 保存所有字段
// 查询
var user User
db.First(&user, 1)
// 修改
user.Name = "李四"
user.Age = 20
// 保存(更新所有字段)
db.Save(&user)
// UPDATE users SET name='李四', age=20, updated_at='2024-01-01' WHERE id=1;1.2 Update 更新单个字段
// 更新单个字段
db.Model(&User{}).Where("id = ?", 1).Update("name", "李四")
// UPDATE users SET name='李四', updated_at='2024-01-01' WHERE id=1;
// 使用结构体
db.Model(&user).Update("name", "李四")1.3 Updates 更新多个字段
// 使用 map
db.Model(&User{}).Where("id = ?", 1).Updates(map[string]interface{}{
"name": "李四",
"age": 20,
})
// 使用结构体(只更新非零值)
db.Model(&user).Updates(User{Name: "李四", Age: 20})
// 更新零值需要使用 Select
db.Model(&user).Select("Age").Updates(User{Age: 0})1.4 批量更新
// 更新所有记录
db.Model(&User{}).Updates(User{Status: 1})
// 条件批量更新
db.Model(&User{}).Where("age > ?", 18).Updates(map[string]interface{}{
"status": 1,
"level": 2,
})二、更新选项
2.1 Select 指定字段
// 只更新指定字段
db.Model(&user).Select("name", "age").Updates(User{
Name: "李四",
Age: 20,
Email: "test@example.com", // 不会更新
})2.2 Omit 忽略字段
// 忽略指定字段
db.Model(&user).Omit("email").Updates(User{
Name: "李四",
Age: 20,
Email: "test@example.com", // 不会更新
})2.3 更新零值
// 方式1:使用 Select
db.Model(&user).Select("age").Updates(User{Age: 0})
// 方式2:使用 map
db.Model(&user).Updates(map[string]interface{}{"age": 0})
// 方式3:使用 UpdateColumn(跳过钩子)
db.Model(&user).UpdateColumn("age", 0)三、SQL 表达式更新
3.1 使用表达式
import "gorm.io/gorm/clause"
// 年龄加1
db.Model(&user).Update("age", gorm.Expr("age + ?", 1))
// 使用 clause.Expr
db.Model(&user).Updates(map[string]interface{}{
"age": clause.Expr{SQL: "age + ?", Vars: []interface{}{1}},
"score": clause.Expr{SQL: "score * ?", Vars: []interface{}{2}},
})3.2 子查询更新
db.Model(&User{}).
Where("role = ?", "admin").
Update("level", db.Model(&Level{}).Select("max_level").Where("role = ?", "admin"))四、更新钩子
4.1 BeforeUpdate
func (u *User) BeforeUpdate(tx *gorm.DB) error {
// 更新前执行
if u.Age < 0 {
return errors.New("年龄不能为负数")
}
return nil
}4.2 AfterUpdate
func (u *User) AfterUpdate(tx *gorm.DB) error {
// 更新后执行
log.Printf("用户 %s 更新成功", u.Name)
return nil
}4.3 跳过钩子
// UpdateColumn 跳过钩子
db.Model(&user).UpdateColumn("age", 20)
// UpdateColumns 跳过钩子
db.Model(&user).UpdateColumns(User{Name: "李四", Age: 20})五、删除记录
5.1 删除单条记录
// 根据主键删除
db.Delete(&User{}, 1)
// DELETE FROM users WHERE id = 1;
// 根据条件删除
db.Where("name = ?", "张三").Delete(&User{})
// DELETE FROM users WHERE name = '张三';5.2 批量删除
// 删除所有记录(需要添加条件)
db.Where("1 = 1").Delete(&User{})
// 条件批量删除
db.Where("age < ?", 18).Delete(&User{})
// 使用主键批量删除
db.Delete(&User{}, []int{1, 2, 3})
// DELETE FROM users WHERE id IN (1,2,3);5.3 阻止全局删除
// 没有条件会报错
db.Delete(&User{})
// Error: WHERE conditions required
// 必须添加条件
db.Where("1 = 1").Delete(&User{})六、软删除
6.1 启用软删除
type User struct {
ID uint
Name string
DeletedAt gorm.DeletedAt `gorm:"index"`
}
// 删除(软删除)
db.Delete(&user)
// UPDATE users SET deleted_at='2024-01-01' WHERE id=1;
// 查询时自动过滤软删除记录
db.Find(&users)
// SELECT * FROM users WHERE deleted_at IS NULL;6.2 查询软删除记录
// 包含软删除记录
db.Unscoped().Find(&users)
// 只查询软删除记录
db.Unscoped().Where("deleted_at IS NOT NULL").Find(&users)6.3 永久删除
// 永久删除
db.Unscoped().Delete(&user)
// DELETE FROM users WHERE id=1;6.4 恢复软删除记录
// 恢复
db.Model(&user).Unscoped().Update("deleted_at", nil)七、删除钩子
7.1 BeforeDelete
func (u *User) BeforeDelete(tx *gorm.DB) error {
// 删除前执行
if u.Role == "admin" {
return errors.New("不能删除管理员")
}
return nil
}7.2 AfterDelete
func (u *User) AfterDelete(tx *gorm.DB) error {
// 删除后执行
log.Printf("用户 %s 已删除", u.Name)
// 删除关联数据
return tx.Where("user_id = ?", u.ID).Delete(&Profile{}).Error
}八、级联删除
8.1 手动级联删除
func (s *UserService) Delete(userID uint) error {
return s.db.Transaction(func(tx *gorm.DB) error {
// 删除用户的文章
if err := tx.Where("user_id = ?", userID).Delete(&Article{}).Error; err != nil {
return err
}
// 删除用户的评论
if err := tx.Where("user_id = ?", userID).Delete(&Comment{}).Error; err != nil {
return err
}
// 删除用户
if err := tx.Delete(&User{}, userID).Error; err != nil {
return err
}
return nil
})
}8.2 使用钩子级联删除
func (u *User) AfterDelete(tx *gorm.DB) error {
// 删除关联数据
if err := tx.Where("user_id = ?", u.ID).Delete(&Article{}).Error; err != nil {
return err
}
if err := tx.Where("user_id = ?", u.ID).Delete(&Comment{}).Error; err != nil {
return err
}
return nil
}九、返回数据
9.1 Returning
// PostgreSQL 支持
var users []User
db.Clauses(clause.Returning{}).Where("age > ?", 18).Delete(&users)
// 返回被删除的记录十、完整示例
10.1 用户更新服务
type UpdateUserDTO struct {
Name *string
Email *string
Age *int
Nickname *string
}
func (s *UserService) Update(userID uint, dto UpdateUserDTO) error {
updates := make(map[string]interface{})
if dto.Name != nil {
updates["name"] = *dto.Name
}
if dto.Email != nil {
// 检查邮箱是否已存在
var count int64
s.db.Model(&User{}).
Where("email = ? AND id != ?", *dto.Email, userID).
Count(&count)
if count > 0 {
return errors.New("邮箱已被使用")
}
updates["email"] = *dto.Email
}
if dto.Age != nil {
updates["age"] = *dto.Age
}
if dto.Nickname != nil {
updates["nickname"] = *dto.Nickname
}
return s.db.Model(&User{}).Where("id = ?", userID).Updates(updates).Error
}10.2 批量更新状态
func (s *UserService) BatchUpdateStatus(userIDs []uint, status int) error {
return s.db.Model(&User{}).
Where("id IN ?", userIDs).
Update("status", status).Error
}10.3 软删除与恢复
func (s *UserService) SoftDelete(userID uint) error {
return s.db.Delete(&User{}, userID).Error
}
func (s *UserService) Restore(userID uint) error {
return s.db.Model(&User{}).
Unscoped().
Where("id = ?", userID).
Update("deleted_at", nil).Error
}
func (s *UserService) HardDelete(userID uint) error {
return s.db.Unscoped().Delete(&User{}, userID).Error
}10.4 增量更新
func (s *ArticleService) IncrementViewCount(articleID uint) error {
return s.db.Model(&Article{}).
Where("id = ?", articleID).
Update("view_count", gorm.Expr("view_count + ?", 1)).Error
}
func (s *UserService) IncrementScore(userID uint, points int) error {
return s.db.Model(&User{}).
Where("id = ?", userID).
Updates(map[string]interface{}{
"score": gorm.Expr("score + ?", points),
"updated_at": time.Now(),
}).Error
}十一、性能优化
11.1 批量更新优化
// 使用事务批量更新
db.Transaction(func(tx *gorm.DB) error {
for _, user := range users {
if err := tx.Model(&user).Updates(user).Error; err != nil {
return err
}
}
return nil
})11.2 跳过钩子提升性能
// 跳过钩子
db.Session(&gorm.Session{SkipHooks: true}).
Model(&User{}).
Where("status = ?", 0).
Update("status", 1)十二、常见问题
12.1 更新零值
// 错误:零值不会更新
db.Model(&user).Updates(User{Age: 0})
// 正确:使用 Select
db.Model(&user).Select("age").Updates(User{Age: 0})
// 或使用 map
db.Model(&user).Updates(map[string]interface{}{"age": 0})12.2 批量更新返回影响行数
result := db.Model(&User{}).Where("age > ?", 18).Update("status", 1)
fmt.Println("影响行数:", result.RowsAffected)12.3 软删除查询
// 默认不包含软删除记录
db.Find(&users)
// 包含软删除记录
db.Unscoped().Find(&users)