GORM 入门笔记(五)简单的增删改查
本篇使用以下示例用表
1 | type User struct { |
创建
单独创建
使用 Create()
方法即可
1 | GLOBAL_DB.Create(&User{Name: "张三", Age: 18}) |
而如何知道有没有创建成功呢?
观察可以发现,这个方法会返回 DB 对象
1 | // DB GORM DB definition |
而 DB 有 Error
和 RowsAffected
这两个属性,就可以很方便地得到结果
1 | func CreateUser() { |
可以看见并没有报错
那么如果报错是怎样的呢?可以添加一个 NOT NULL 的字段,然后不提供值试一下
1 | type User struct { |
可以看见报错信息
创建时指定字段
在 Create
之前使用 Select
方法可以只会传递被选中的字段值(没被选中的如果有默认值就会被赋成默认)
下面的例子只会创建 Name
而不会有 Age
1 | GLOBAL_DB.Select("Name").Create(&User{Name: "张三", Age: 18}) |
创建时跳过字段
类似地,还可以使用 Omit
跳过字段,下面的例子只会创建 Age
而不会有 Name
1 | GLOBAL_DB.Omit("Name").Create(&User{Name: "张三", Age: 18}) |
批量创建
可以使用切片来批量创建
1 | GLOBAL_DB.Create(&[]User{ |
查询
我感觉 GROM 的查询玩的太花了,方法(字面义)很多,这里只讲一部分
查询主键排序后的第一条
使用 First()
方法
查询时有两种方式来接收返回值,分别是 Map 和结构体
在查询的时候必须在语句中体现原始模型,即使是使用 Table("users")
指定表名也不行,所以如果要使用 Map 接收,必须使用 Model(&User{})
手动指定模型
1 | var result = make(map[string]interface{}) |
另外一种是使用模型的结构体,因为结构体本身已经能体现原始模型,所以无需使用 Model
方法
1 | var result User |
查询主键排序后的最后一条
类似地,还有 Last()
方法,不同之处只是拿最后一条
1 | var result User |
查询第一条(不排序)
再类似地,还有 take()
方法
1 | var result User |
这东西在生成 SQL 时不会添加按主键排序的字句,不过在这里结果肯定是一样的
查询多条记录
很简单,把结构体变成切片,然后使用 Find()
或是 Scan()
使用检索条件
使用 Where()
给出条件
String 条件
传入字符串给 Where()
以作为条件,把可以把这东西直接当做 SQL 中的 WHERE
字句,什么 AND
NOT
OR
LIKE
都可以用,而且可以使用 ?
作为占位符,格式化字符串
1 | var result []User |
在字符串里可以像 SQL 一样使用 AND
和 OR
关键字
1 | var result []User |
当然了,如果只想查一个的话,使用 First()
就行
Struct & Map 条件
这个不常用,建议去官方文档看
Or()
和 Not()
上面的
1 | GLOBAL_DB.Where("name = ? OR age = 22", "张三").Find(&result) |
还可以这样写
1 | GLOBAL_DB.Where("name = ?", "张三").Or("age = 22").Find(&result) |
等于说是抽离出来了,结果是一样的
而 Not()
可以这样用
1 | GLOBAL_DB.Not("name = ?", "张三").Find(&result) |
为什么 NULL 不会被选中?我想因为 GORM 是转换成 SQL 去查询的,而 SQL 去查询 MySQL 的时候,NULL 就是不会被选中
内联条件
在查询动作方法中直接加上 Where()
中的内容
1 | GLOBAL_DB.Find(&result, "name = ?", "张三") |
这个应该没什么好说的
主键检索
如果主键是数字类型,可以使用简化的内联条件
例如下面查找主键为 4 的记录
1 | var result User |
类似地,还有下面的用法
1 | var result []User |
如果你的主键是字符串(例如 UUID),就必须写成正式的内联查询或使用 Where()
1 | GLOBAL_DB.First(&result, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a") |
而不能这么写
1 | GLOBAL_DB.First(&result,"1b74413f-f3b8-409f-ac47-e8c062e3472a") |
选择特定字段
使用 Select()
选择你想要的字段
1 | GLOBAL_DB.Select("name", "age").Find(&result) |
这时其他的字段都没有获取到,全是零值
智能选择字段
使用你想要的字段定义结构体,然后用它来收集结果
因为无法得知原始模型,所以需要加上 Model()
方法
1 | type SampleUser struct { |
这样就很简洁了
Limit & Offset
这个很简单,直接用,不懂的去看文档吧
排序
这个也看文档吧
更新
更新的话只是把上面查找的方法全变成更新的方法
等于就是选中集合,上面最后是查找,但是现在是更新
Update()
只更新你选择的字段
1 | GLOBAL_DB.Model(&User{}).Where("name = ?", "张三").Update("age", 19) |
Save()
将查询的结果先暂存,更改后再提交
无论如何都会更新,包括零值
1 | var result User |
可以更改多行
1 | var result []User |
Updates()
更新所有字段,有两种形式
Struct
若使用结构体,零值不参与更新
1 | var result User |
本来是可以同时更新两个列的,但是因为 0 是整型的零值,所以不会更新 Age
Map
但是如果使用 Map,即使是零值也会更新
1 | var result User |
如果要同时更新多行,只需将结果集变成切片
1 | var result []User |
删除
删除只有 Delete()
一个方法,默认是软删除(仅写入 delete_at
字段)
1 | GLOBAL_DB.Where("name = ?", "").Delete(&User{}) |
删除所有名字为空的记录
如果想要硬删除,可以加上 Unscoped()
方法
1 | GLOBAL_DB.Unscoped().Where("name = ?", "").Delete(&User{}) |
原生SQL
若要使用原生 SQL 查询 ,末尾只能为 Scan()
1 | var result User |
错误检查
对于可能产生错误的语句,应当始终进行错误检查
例如按照不存在的主键进行查询
1 | dbRes := GLOBAL_DB.First(&result, 6) |
它的结果会是空,然后返回 record not found
错误
业务中会将拿到的错误与 GORM 中预定义的错误进行比较,就像下面这样
1 | errors.Is(dbRes.Error, gorm.ErrRecordNotFound) |