前言:gorm是Golang语言中一款性能极好的ORM库,对开发人员相对是比较友好的。当然还有另外一个xorm库也是比较出名的,感兴趣的也可以看看这个库,接下来主要介绍下gorm库的一些基本使用。

GORM介绍和快速入门

功能概览

  • 全功能ORM(无限接近)
  • 关联(Has One, Has Many, Belongs To, Many To Many, 多态)
  • 钩子函数Hook(在创建/保存/更新/删除/查找之前或之后)
  • 预加载
  • 事务
  • 复合主键
  • SQL 生成器
  • 数据库自动迁移
  • 自定义日志
  • 可扩展性, 可基于 GORM 回调编写插件
  • 所有功能都被测试覆盖
  • 开发者友好

安装
我们都知道,在golang中需要使用一些驱动包来对指定数据库进行操作,比如MySQL需要使用github.com/go-sql-driver/mysql库,而Sqlite需要使用github.com/mattn/go-sqlite3库来支持,不过好在gorm框架中对各个驱动包进行了简单包装,可以让我们在写程序时可以更方便的管理驱动库.
支持的数据库以及导入路径如下:

注意:gorm框架只是简单封装了数据库的驱动包,在安装时仍需要下载原始的驱动包

  1. # 由于在项目中使用mysql比较多,这里使用mysql进行数据存储
  2. $ go get -u github.com/jinzhu/gorm
  3. $ go get -u github.com/go-sql-driver/mysql

快速入门

  1. # 使用docker快速创建一个本地可连接的mysql实例
  2. $ docker run -itd -e MYSQL_ROOT_PASSWORD='bgbiao.top' --name go-orm-mysql -p 13306:3306 mysql:5.6
  3. # 登陆mysql并创建一个测试库
  4. $ docker exec -it go-orm-mysql mysql -uroot -pbgbiao.top
  5. ....
  6. mysql> create database test_api;
  7. Query OK, 1 row affected (0.00 sec)
  8. mysql> show databases;
  9. +--------------------+
  10. | Database |
  11. +--------------------+
  12. | information_schema |
  13. | mysql |
  14. | performance_schema |
  15. | test_api |
  16. +--------------------+
  17. 4 rows in set (0.00 sec)
  18. # 运行一个简单示例
  19. $ cat gorm-mysql-example.go
  20. package main
  21. import (
  22. "fmt"
  23. "time"
  24. "github.com/jinzhu/gorm"
  25. _ "github.com/jinzhu/gorm/dialects/mysql"
  26. )
  27. // 定义一个数据模型(user表)
  28. // 列名是字段名的蛇形小写(PassWd->pass_word)
  29. type User struct {
  30. Id uint `gorm:"AUTO_INCREMENT"`
  31. Name string `gorm:"size:50"`
  32. Age int `gorm:"size:3"`
  33. Birthday *time.Time
  34. Email string `gorm:"type:varchar(50);unique_index"`
  35. PassWord string `gorm:"type:varchar(25)"`
  36. }
  37. var db *gorm.DB
  38. func main() {
  39. db,err := gorm.Open("mysql","root:bgbiao.top@(127.0.0.1:13306)/test_api?charset=utf8&parseTime=True&loc=Local")
  40. if err != nil {
  41. fmt.Errorf("创建数据库连接失败:%v",err)
  42. }
  43. defer db.Close()
  44. // 自动迁移数据结构(table schema)
  45. // 注意:在gorm中,默认的表名都是结构体名称的复数形式,比如User结构体默认创建的表为users
  46. // db.SingularTable(true) 可以取消表名的复数形式,使得表名和结构体名称一致
  47. db.AutoMigrate(&User{})
  48. // 添加唯一索引
  49. db.Model(&User{}).AddUniqueIndex("name_email", "id", "name","email")
  50. // 插入记录
  51. db.Create(&User{Name:"bgbiao",Age:18,Email:"bgbiao@bgbiao.top"})
  52. db.Create(&User{Name:"xxb",Age:18,Email:"xxb@bgbiao.top"})
  53. var user User
  54. var users []User
  55. // 查看插入后的全部元素
  56. fmt.Printf("插入后元素:\n")
  57. db.Find(&users)
  58. fmt.Println(users)
  59. // 查询一条记录
  60. db.First(&user,"name = ?","bgbiao")
  61. fmt.Println("查看查询记录:",user)
  62. // 更新记录(基于查出来的数据进行更新)
  63. db.Model(&user).Update("name","biaoge")
  64. fmt.Println("更新后的记录:",user)
  65. // 删除记录
  66. db.Delete(&user)
  67. // 查看全部记录
  68. fmt.Println("查看全部记录:")
  69. db.Find(&users)
  70. fmt.Println(users)
  71. }
  72. # 运行gorm实例
  73. $ go run gorm-mysql-example.go
  74. 插入后元素:
  75. [{1 bgbiao 18 <nil> bgbiao@bgbiao.top } {2 xxb 18 <nil> xxb@bgbiao.top }]
  76. 查看查询记录: {1 bgbiao 18 <nil> bgbiao@bgbiao.top }
  77. 更新后的记录: {1 biaoge 18 <nil> bgbiao@bgbiao.top }
  78. 查看全部记录:
  79. [{2 xxb 18 <nil> xxb@bgbiao.top }]

GORM常用的功能函数

自动迁移
注意: 使用自动迁移模式将保持表的更新,但是不会更新索引以及现有列的类型或删除未使用的列

  1. // 同时迁移多个模型
  2. db.AutoMigrate(&User{}, &Product{}, &Order{})
  3. // 创建表时增加相关参数
  4. // 比如修改表的字符类型CHARSET=utf8
  5. db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})

检查表

  1. // 检查模型是否存在
  2. db.HasTable(&User{})
  3. // 检查表是否存在
  4. db.HasTable("users")

增、删、改表结构
注意:在实际企业的生产环境中,通常数据库级别的变更操作,都需要转换成sql交由DBA兄弟帮忙进行线上库表变更,因此不论使用自动迁移,还是手动创建表,都是在开发环境阶段的

  1. // 使用模型创建
  2. db.CreateTable(&User{})
  3. // 增加参数创建
  4. db.Set("gorm:table_options", "ENGINE=InnoDB").CreateTable(&User{})
  5. // 删除表
  6. db.DropTable(&User{})
  7. db.DropTable("users")
  8. // 模型和表名的混搭
  9. db.DropTableIfExists(&User{}, "products")
  10. // 修改列(修改字段类型)
  11. db.Model(&User{}).ModifyColumn("description", "text")
  12. // 删除列
  13. db.Model(&User{}).DropColumn("description")
  14. // 指定表名创建表
  15. db.Table("deleted_users").CreateTable(&User{})
  16. // 指定表名查询
  17. var deleted_users []User
  18. db.Table("deleted_users").Find(&deleted_users)

索引和约束

  1. // 添加外键
  2. // 1st param : 外键字段
  3. // 2nd param : 外键表(字段)
  4. // 3rd param : ONDELETE
  5. // 4th param : ONUPDATE
  6. db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT")
  7. // 单个索引
  8. db.Model(&User{}).AddIndex("idx_user_name", "name")
  9. // 多字段索引
  10. db.Model(&User{}).AddIndex("idx_user_name_age", "name", "age")
  11. // 添加唯一索引(通常使用多个字段来唯一标识一条记录)
  12. db.Model(&User{}).AddUniqueIndex("idx_user_name", "name")
  13. db.Model(&User{}).AddUniqueIndex("idx_user_name_age", "name", "id","email")
  14. // 删除索引
  15. db.Model(&User{}).RemoveIndex("idx_user_name")

GORM模型注意事项

gorm.Model结构体
其实在gorm官方文档的示例中,会默认在模型的属性中增加一个gorm.Model的属性,该属性的原始结构如下:

  1. // 官方示例模型
  2. type User struct {
  3. gorm.Model
  4. Name string
  5. .....
  6. }
  7. // gorm.Model结构体
  8. type Model struct {
  9. ID uint `gorm:"primary_key"`
  10. CreatedAt time.Time
  11. UpdatedAt time.Time
  12. DeletedAt *time.Time `sql:"index"`
  13. }

很明显,当我们在用户自定义的模型中增加gorm.Model时,会自动为我们的表增加id,created_at,updated_at,deleted_at四个字段。
同时需要注意的是,当我们的模型中有CreatedAt,UpdatedAt,DeletedAt属性并且类型为time.Time或者time.Time类型时,当有数据操作时,会自动更新对应的时间。
所以,在定义模型时,可以根据实际的需求来决定是否引入gorm.Model结构
另外需要注意的是: 所有字段的零值, 比如0, ‘’, false 或者其它零值都不会保存到数据库内,但会使用他们的默认值,因此一些非必须字段,可以使用DEFAULT的tag字段来声明列的默认值。
gorm模型支持的tags
注意:gorm支持一些常见的tags来自定义模型字段的扩展信息
*结构体标记描述
column列明(默认是字段的蛇形小写)type数据类型size列长度PRIMARY_KEY声明主键UNIQUE声明唯一DEFAULT指定默认值PRECISION声明列精度NOT NULL将列指定为非 NULLAUTO_INCREMENT声明自增列INDEX创建具有或不带名称的索引, 如果多个索引同名则创建复合索引UNIQUE_INDEX创建唯一索引EMBEDDED将结构设置为嵌入EMBEDDED_PREFIX设置嵌入结构的前缀-忽略此字段
注意: gorm也支持一些关联的结构体标签,比如外键,关联外键,等操作,通常在复杂的企业环境中,建议在库表设计时将相关表都设计成孤立表,具体的关联逻辑由业务层去实现(可能增加了开发的成本,不过当业务发展比较复杂时,这样做无疑是方便后期做扩展和优化的)

详细的CRUD接口

创建

插入记录

  1. // 相当于insert into users(name,age,brithday) values("BGBiao",18,time.Now())
  2. user := User{Name: "BGBiao", Age: 18, Birthday: time.Now()}
  3. // 主键为空返回`true`
  4. db.NewRecord(user)
  5. db.Create(&user)
  6. // 创建`user`后返回`false`
  7. db.NewRecord(user)

在Hooks中设置字段值
注意: 通常我们在设计模型时,有一些原始字段,希望在初始化模型后就拥有记录,此时可以使用hooks来插入初始记录
如果想再BeforeCreatehook中修改字段的值,可以使用scope.SetColumn:

  1. func (user *User) BeforeCreate(scope *gorm.Scope) error {
  2. scope.SetColumn("ID", uuid.New())
  3. return nil
  4. }

创建扩展选项

  1. // 为Instert语句添加扩展SQL选项
  2. // insert into produce(name,code) values("name","code") on conflict;
  3. db.Set("gorm:insert_option", "ON CONFLICT").Create(&product)

查询

基本查询

  1. // 根据主键查询第一条记录
  2. // SELECT * FROM users ORDER BY id LIMIT 1;
  3. db.First(&user)
  4. // 随机获取一条记录
  5. // SELECT * FROM users LIMIT 1;
  6. db.Take(&user)
  7. // 根据主键查询最后一条记录
  8. // SELECT * FROM users ORDER BY id DESC LIMIT 1;
  9. db.Last(&user)
  10. // 查询所有的记录
  11. // SELECT * FROM users;
  12. db.Find(&users)
  13. // 查询指定的某条记录(仅当主键为整型时可用)
  14. // SELECT * FROM users WHERE id = 10;
  15. db.First(&user, 10)

结构体方式查询

  1. // 结构体方式
  2. // select * from users where name = 'bgbiao.top'
  3. db.Where(&User{Name: "bgbiao.top", Age: 20}).First(&user)
  4. // Map方式
  5. // select * from users where name = 'bgbiao.top' and age = 20;
  6. db.Where(map[string]interface{}{"name": "bgbiao.top", "age": 20}).Find(&users)
  7. // 主键的切片
  8. // select * from users where id in (20,21,22);
  9. db.Where([]int64{20, 21, 22}).Find(&users)

Where条件查询
注意: 使用了Where()方法,基本上就是基本的sql语法

  1. // 使用条件获取一条记录 First()方法
  2. db.Where("name = ?", "bgbiao.top").First(&user)
  3. // 获取全部记录 Find()方法
  4. db.Where("name = ?", "jinzhu").Find(&users)
  5. // 不等于
  6. db.Where("name <> ?", "jinzhu").Find(&users)
  7. // IN
  8. db.Where("name IN (?)", []string{"jinzhu", "bgbiao.top"}).Find(&users)
  9. // LIKE
  10. db.Where("name LIKE ?", "%jin%").Find(&users)
  11. // AND
  12. db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
  13. // Time
  14. // select * from users where updated_at > '2020-03-06 00:00:00'
  15. db.Where("updated_at > ?", lastWeek).Find(&users)
  16. // BETWEEN
  17. // select * from users where created_at between '2020-03-06 00:00:00' and '2020-03-14 00:00:00'
  18. db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)

Not条件

  1. // select * from users where name != 'bgbiao.top';
  2. db.Not("name", "jinzhu").First(&user)
  3. // Not In
  4. // select * from users where name not in ("jinzhu","bgbiao.top");
  5. db.Not("name", []string{"jinzhu", "bgbiao.top"}).Find(&users)
  6. // 主键不在slice中的查询
  7. // select * from users where id not in (1,2,3)
  8. db.Not([]int64{1,2,3}).First(&user)
  9. // select * from users;
  10. db.Not([]int64{}).First(&user)
  11. // 原生SQL
  12. // select * from users where not(name = 'bgbiao.top');
  13. db.Not("name = ?", "bgbiao.top").First(&user)
  14. // struct方式查询
  15. // select * from users where name != 'bgbiao.top'
  16. db.Not(User{Name: "bgbiao.top"}).First(&user)

Or条件

  1. // SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';
  2. db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
  3. // Struct 方式
  4. // SELECT * FROM users WHERE name = 'jinzhu' OR name = 'bgbiao.top';
  5. db.Where("name = 'jinzhu'").Or(User{Name: "bgbiao.top"}).Find(&users)
  6. // Map 方式
  7. // SELECT * FROM users WHERE name = 'jinzhu' OR name = 'bgbiao.top';
  8. db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "bgbiao.top"}).Find(&users)

FirstOrCreate
获取匹配的第一条记录, 否则根据给定的条件创建一个新的记录(仅支持 struct 和 map 条件)

  1. // 未找到,就插入记录
  2. if select * from users where name = 'non_existing') is null; insert into users(name) values("non_existing")
  3. db.FirstOrCreate(&user, User{Name: "non_existing"})
  4. // 找到
  5. // select * from users where name = 'bgbiao.top'
  6. db.Where(User{Name: "bgbiao.top"}).FirstOrCreate(&user)
  7. ## attrs参数:如果记录未找到,将使用参数创建 struct 和记录.
  8. // 未找到(将条件前置,并将拆入数据填充到Attrs方法中)
  9. db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
  10. // 找到
  11. db.Where(User{Name: "bgbiao.top"}).Attrs(User{Age: 30}).FirstOrCreate(&user)
  12. ## assgin参数: 不管记录是否找到,都将参数赋值给 struct 并保存至数据库
  13. db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)

子查询
*gorm.expr 子查询

  1. // SELECT * FROM "orders" WHERE "orders"."deleted_at" IS NULL AND (amount > (SELECT AVG(amount) FROM "orders" WHERE (state = 'paid')));
  2. db.Where("amount > ?", DB.Table("orders").Select("AVG(amount)").Where("state = ?", "paid").QueryExpr()).Find(&orders)

字段查询
通常情况下,我们只想选择几个字段进行查询,指定你想从数据库中检索出的字段,默认会选择全部字段。

  1. // SELECT name, age FROM users;
  2. db.Select("name, age").Find(&users)
  3. // SELECT name, age FROM users;
  4. db.Select([]string{"name", "age"}).Find(&users)
  5. // SELECT COALESCE(age,'42') FROM users;
  6. db.Table("users").Select("COALESCE(age,?)", 42).Rows()

排序(Order)

  1. // SELECT * FROM users ORDER BY age desc, name;
  2. db.Order("age desc, name").Find(&users)
  3. // 多字段排序
  4. // SELECT * FROM users ORDER BY age desc, name;
  5. db.Order("age desc").Order("name").Find(&users)
  6. // 覆盖排序
  7. //
  8. db.Order("age desc").Find(&users1).Order("age", true).Find(&users2)

限制输出

  1. // SELECT * FROM users LIMIT 3;
  2. db.Limit(3).Find(&users)
  3. // -1 取消 Limit 条件
  4. // SELECT * FROM users LIMIT 10;
  5. // SELECT * FROM users;
  6. db.Limit(10).Find(&users1).Limit(-1).Find(&users2)

统计count

  1. // SELECT count(*) from USERS WHERE name = 'jinzhu' OR name = 'bgbiao.top';
  2. db.Where("name = ?", "jinzhu").Or("name = ?", "bgbiao.top").Find(&users).Count(&count)
  3. // select count(*) from users where name = 'bgbiao.top'
  4. db.Model(&User{}).Where("name = ?", "bgbiao.top").Count(&count)
  5. // SELECT count(*) FROM deleted_users;
  6. db.Table("deleted_users").Count(&count)
  7. // SELECT count( distinct(name) ) FROM deleted_users;
  8. db.Table("deleted_users").Select("count(distinct(name))").Count(&count)

分组(grouo&having)

  1. rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
  2. for rows.Next() {
  3. ...
  4. }
  5. rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
  6. for rows.Next() {
  7. ...
  8. }
  9. type Result struct {
  10. Date time.Time
  11. Total int64
  12. }
  13. db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)

连接查询

  1. rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
  2. for rows.Next() {
  3. ...
  4. }
  5. db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)
  6. // 多连接及参数
  7. db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)

pluck查询
Pluck,查询 model 中的一个列作为切片,如果您想要查询多个列,您应该使用Scan

  1. var ages []int64
  2. db.Find(&users).Pluck("age", &ages)
  3. var names []string
  4. db.Model(&User{}).Pluck("name", &names)
  5. db.Table("deleted_users").Pluck("name", &names)

scan扫描

  1. type Result struct {
  2. Name string
  3. Age int
  4. }
  5. var result Result
  6. db.Table("users").Select("name, age").Where("name = ?", "Antonio").Scan(&result)
  7. // 原生 SQL
  8. db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)

更新

更新所有字段
注意:save会更新所有字段,及时没有赋值

  1. db.First(&user)
  2. user.Name = "bgbiao.top"
  3. user.Age = 100
  4. // update users set name = 'bgbiao.top',age=100 where id = user.id
  5. db.Save(&user)

更新修改字段
使用Update和Updates方法

  1. // 更新单个属性,如果它有变化
  2. // update users set name = 'hello' where id = user.id
  3. db.Model(&user).Update("name", "hello")
  4. // 根据给定的条件更新单个属性
  5. // update users set name = 'hello' where active = true
  6. db.Model(&user).Where("active = ?", true).Update("name", "hello")
  7. // 使用 map 更新多个属性,只会更新其中有变化的属性
  8. // update users set name = 'hello',age=18,actived=false where id = user.id
  9. db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
  10. // 使用 struct 更新多个属性,只会更新其中有变化且为非零值的字段
  11. db.Model(&user).Updates(User{Name: "hello", Age: 18})
  12. // 警告:当使用 struct 更新时,GORM只会更新那些非零值的字段
  13. // 对于下面的操作,不会发生任何更新,"", 0, false 都是其类型的零值
  14. db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false})

更新选定字段
如果想要更新或者忽略某些字段,可以先使用Select或Omit

  1. // update users set name = 'hello' where id = user.id;
  2. db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
  3. // Omit()方法用来忽略字段
  4. // update users set age=18,actived=false where id = user.id
  5. db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})

无hook更新
上面的更新操作会会自动运行 model 的BeforeUpdate,AfterUpdate方法,来更新一些类似UpdatedAt的字段在更新时保存其 Associations, 如果你不想调用这些方法,你可以使用UpdateColumn,UpdateColumns

  1. // 更新单个属性,类似于 `Update`
  2. // update users set name = 'hello' where id = user.id;
  3. db.Model(&user).UpdateColumn("name", "hello")
  4. // 更新多个属性,类似于 `Updates`
  5. // update users set name = 'hello',age=18 where id = user.id;
  6. db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18})

批量更新
注意: 使用struct实例更新时,只会更新非零值字段,弱项更新全部字段,建议使用map

  1. // update users set name = 'hello',age=18 where id in (10,11)
  2. db.Table("users").Where("id IN (?)", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
  3. // 使用 struct 更新时,只会更新非零值字段,若想更新所有字段,请使用map[string]interface{}
  4. db.Model(User{}).Updates(User{Name: "hello", Age: 18})
  5. // 使用 `RowsAffected` 获取更新记录总数
  6. db.Model(User{}).Updates(User{Name: "hello", Age: 18}).RowsAffected

使用SQL计算表达式

  1. // update products set price = price*2+100 where id = product.id
  2. DB.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))
  3. // update products set price = price*2+100 where id = product.id;
  4. DB.Model(&product).Updates(map[string]interface{}{"price": gorm.Expr("price * ? + ?", 2, 100)})
  5. // update products set quantity = quantity-1 where id = product.id;
  6. DB.Model(&product).UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
  7. // update products set quantity = quantity -1 where id = product.id and quantity > 1
  8. DB.Model(&product).Where("quantity > 1").UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))

删除

删除记录
注意: 删除记录时,请确保主键字段有值,GORM 会通过主键去删除记录,如果主键为空,GORM 会删除该 model 的所有记录。

  1. // 删除现有记录
  2. // delete from emails where id = email.id;
  3. db.Delete(&email)
  4. // 为删除 SQL 添加额外的 SQL 操作
  5. // delete from emails where id = email.id OPTION (OPTIMIZE FOR UNKNOWN)
  6. db.Set("gorm:delete_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&email)

批量删除

  1. // delete from emails where email like '%jinzhu%'
  2. db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
  3. // 同上
  4. db.Delete(Email{}, "email LIKE ?", "%jinzhu%")

软删除
如果一个model有DeletedAt字段,他将自动获得软删除的功能!
当调用Delete方法时, 记录不会真正的从数据库中被删除,只会将DeletedAt字段的值会被设置为当前时间。
在之前,我们可能会使用isDelete之类的字段来标记记录删除,不过在gorm中内置了DeletedAt字段,并且有相关hook来保证软删除。

  1. // UPDATE users SET deleted_at="2020-03-13 10:23" WHERE id = user.id;
  2. db.Delete(&user)
  3. // 批量删除
  4. // 软删除的批量删除其实就是把deleted_at改成当前时间,并且在查询时无法查到,所以底层用的是update的sql
  5. db.Where("age = ?", 20).Delete(&User{})
  6. // 查询记录时会忽略被软删除的记录
  7. // SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
  8. db.Where("age = 20").Find(&user)
  9. // Unscoped 方法可以查询被软删除的记录
  10. // SELECT * FROM users WHERE age = 20;
  11. db.Unscoped().Where("age = 20").Find(&users)

物理删除
注意: 使用Unscoped().Delete()方法才是真正执行sql中的delete语句.

  1. // Unscoped 方法可以物理删除记录
  2. // DELETE FROM orders WHERE id=10;
  3. db.Unscoped().Delete(&order)