乐闻世界logo
搜索文章和话题

How does Soft Delete work in GORM?

3月6日 21:37

GORM supports Soft Delete functionality, allowing records to be logically deleted without actually removing them from the database.

Basic Concept of Soft Delete

Soft delete is implemented by adding a DeletedAt field to the model. When a delete operation is performed, GORM does not actually delete the record but sets the DeletedAt field to the current time.

Basic Usage

Enable Soft Delete

go
type User struct { gorm.Model Name string Email string } // gorm.Model includes the DeletedAt gorm.DeletedAt field

Manually Define Soft Delete Field

go
type User struct { ID uint `gorm:"primaryKey"` Name string DeletedAt gorm.DeletedAt `gorm:"index"` }

Soft Delete Operations

Delete Records

go
// Soft delete db.Delete(&user) // Batch soft delete db.Where("age < ?", 18).Delete(&User{}) // Soft delete by primary key db.Delete(&User{}, 1)

Query Records

By default, GORM automatically filters out soft-deleted records:

go
// Will not query soft-deleted records var users []User db.Find(&users) // Only query soft-deleted records db.Unscoped().Find(&users) // Query all records (including soft-deleted) db.Unscoped().Find(&users)

How Soft Delete Works

DeletedAt Field

go
type DeletedAt struct { Time time.Time Valid bool }
  • Time: Deletion time
  • Valid: Whether deleted (true means deleted)

SQL Generation

go
// SQL generated for delete operation // UPDATE users SET deleted_at = '2024-01-01 12:00:00' WHERE id = 1 // SQL generated for query operation (automatically adds condition) // SELECT * FROM users WHERE deleted_at IS NULL

Advanced Usage

Find Soft-Deleted Records

go
// Use Unscoped var deletedUsers []User db.Unscoped().Where("deleted_at IS NOT NULL").Find(&deletedUsers) // Or directly query db.Unscoped().Find(&deletedUsers)

Restore Soft-Deleted Records

go
// Restore single record var user User db.Unscoped().First(&user, 1) db.Model(&user).Update("DeletedAt", nil) // Batch restore db.Unscoped().Model(&User{}).Where("deleted_at IS NOT NULL").Update("DeletedAt", nil)

Permanently Delete Records

go
// Permanently delete (actually delete from database) db.Unscoped().Delete(&user) // Batch permanent delete db.Unscoped().Where("age < ?", 18).Delete(&User{})

Check if Record is Soft-Deleted

go
var user User db.First(&user, 1) if user.DeletedAt.Valid { fmt.Println("Record has been soft deleted") } else { fmt.Println("Record is not deleted") }

Soft Delete and Associations

Soft Delete in Association Queries

go
type User struct { gorm.Model Name string Posts []Post } type Post struct { gorm.Model Title string UserID uint } // When querying user, soft-deleted posts are not included var user User db.Preload("Posts").First(&user, 1) // When querying user, include soft-deleted posts db.Preload("Posts", "deleted_at IS NOT NULL").Unscoped().First(&user, 1)

Cascade Soft Delete

go
type User struct { gorm.Model Name string Posts []Post `gorm:"constraint:OnDelete:SET NULL"` } // When deleting user, related posts' UserID will be set to NULL db.Delete(&user)

Custom Soft Delete

Custom Soft Delete Field Name

go
type User struct { ID uint `gorm:"primaryKey"` Name string DeletedAt time.Time `gorm:"index"` IsDeleted bool `gorm:"default:false"` } // Custom soft delete logic func (u *User) BeforeDelete(tx *gorm.DB) error { u.IsDeleted = true return nil }

Use Different Soft Delete Strategy

go
type User struct { ID uint `gorm:"primaryKey"` Name string Status string `gorm:"default:'active'"` } func (u *User) BeforeDelete(tx *gorm.DB) error { u.Status = "deleted" return tx.Save(u).Error }

Best Practices for Soft Delete

1. Data Auditing

go
type User struct { gorm.Model Name string DeletedBy uint } func (u *User) BeforeDelete(tx *gorm.DB) error { // Record who deleted the record u.DeletedBy = getCurrentUserID() return nil }

2. Data Recovery

go
func RestoreUser(db *gorm.DB, userID uint) error { return db.Transaction(func(tx *gorm.DB) error { var user User if err := tx.Unscoped().First(&user, userID).Error; err != nil { return err } return tx.Model(&user).Update("DeletedAt", nil).Error }) }

3. Periodic Cleanup

go
func CleanOldDeletedRecords(db *gorm.DB, days int) error { threshold := time.Now().AddDate(0, 0, -days) return db.Unscoped(). Where("deleted_at < ?", threshold). Delete(&User{}).Error }

Notes

  1. Unique Index: Soft delete fields affect unique index constraints
  2. Performance Impact: Soft delete adds query conditions, which may affect performance
  3. Data Cleanup: Need to periodically clean up old soft-deleted data
  4. Association Queries: Pay attention to soft delete behavior in association queries
  5. Storage Space: Soft delete takes up additional storage space
  6. Business Logic: Ensure business logic correctly handles soft-deleted records

Common Questions

Q: Does soft delete affect unique indexes?

A: Yes, because soft-deleted records still exist in the database and may violate unique index constraints. You can use composite unique indexes to solve this.

Q: How to batch restore soft-deleted records?

A: Use Unscoped() and Update() methods to set DeletedAt to nil.

Q: What's the difference between soft delete and hard delete?

A: Soft delete only marks records as deleted, data still exists; hard delete actually removes records from the database.

Q: How to query records deleted within a specific time range?

A: Use Unscoped() and time range query conditions.

标签:Gorm