这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天
前言
大家好呀,这是我参加青训营伴学笔记创作活动的第 14 天,如存在问题,烦请各位斧正!
其中有一些关键图片超过了最大字符限制,不能上传了,我都使用特殊标记给它标记出来了,如有需要,请联系我。
查询接口
查询
1)检索单个对象
GORM 提供了 First、Take、Last 方法,以便从数据库中检索单个对象。
当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误。
result := db.First(&user) // 获取第一条记录(主键升序),相当于 ORDER BY id LIMIT 1; // result.RowsAffected:返回找到的记录数。 result.Error:returns error。 db.Take(&user) // 获取一条记录,没有指定排序字段,只相当于limit 1 db.Last(&user) // 获取最后一条记录(主键降序)2)检索单个对象-指定Model:
同样的可以通过提供Model类型来使用,如果 model 类型没有定义主键,则按第一个字段排序。
result := map[string]interface{}{} DB.Model(&User{}).First(&result)3)检索单个对象-指定Table:可以指定字段名来使用,这种方式不能用First,但能用Take:
result := map[string]interface{}{} DB.Table("users").Take(&result) 4)获取全部记录:result := db.Find(&users)
条件查询WHERE
1)String条件
(1)
db.Where("name = ?", "jinzhu").First(&user)(2)IN:
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)(3)LIKE:
db.Where("name LIKE ?", "%jin%").Find(&users)(4)AND:
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)(5)TIME类型字段也可以传入符合日期格式的字符串:db.Where("updated_at > ?", lastWeek).Find(&users)
(6)BETWEEN:
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)2)Struct & Map 条件
(1)Struct:
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)(2)Map:db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
(3)主键切片条件,相当于IN:db.Where([]int64{20, 21, 22}).Find(&users)
(4)结构体方式和Map下,只会查询非零值字段。这意味着如果您的字段值为 0、''、false 或其他 零值,该字段不会被用于构建查询条件
3)Not 条件
(1)构建NOT条件,用法与上面的Where用法相同,只不过查询结果是不符合传入条件的。
(2)比如下面的相当于WHERENOTname ="jinzhu":db.Not("name = ?", "jinzhu").First(&user)
4)OR条件
(1)
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)(2)Struct或Map:
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2", Age: 18}).Find(&users)
选择特定字段
1)db.Select("name", "age").Find(&users)
2)db.Select([]string{"name", "age"}).Find(&users)
1)db.Order("age desc, name").Find(&users)
2)db.Order("age desc").Order("name").Find(&users)
Limit & Offset查询
Limit 指定获取记录的最大数量。 Offset 指定在开始返回记录之前要跳过的记录数量。
1)单独limit:db.Limit(3).Find(&users)
2)单独offset:db.Offset(3).Find(&users) 相当于 SELECT * FROM users OFFSET 3;
3)两者结合:db.Limit(10).Offset(5).Find(&users) 相当于 SELECT * FROM users OFFSET 5 LIMIT 10;
4)通过 -1 消除 Offset 条件(Limit同理):
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
// SELECT * FROM users OFFSET 10; (users1)
// SELECT * FROM users;
(users2)
Distinct查询
db.Distinct("name", "age").Order("name, age desc").Find(&results)
智能选择字段
可以使用Select方法选择特定的字段,也可以定义一个较小的结构体,以实现调用 API 时自动选择特定的字段:
type User struct {
ID
uint
Name
string
Age int
Gender string
}
type APIUser struct {
ID
uint
Name string
}
// 查询时会自动选择 `id`, `name` 字段
db.Model(&User{}).Limit(10).Find(&APIUser{}) // SELECT `id`, `name` FROM `users` LIMIT 10
子查询
子查询可以嵌套在查询中,比如:
db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders)
// SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");
结果集处理
1)Rows.Next() 需要关闭
每次返回的rows 都是新的实例,占用一个数据库连接. rows.next()直到false 就会关闭,否则就会出现资源没有被回收,
但数据库设置的超时回收也会去回收 但是会降低服务Tps。
增删改接口
插入
1)插入一条数据
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
result := db.Create(&user) // 通过数据的指针来创建
user.ID
// 返回插入数据的主键
result.Error // 返回 error
result.RowsAffected // 返回插入记录的条数
2)批量插入
将切片数据传递给 Create 方法,GORM 将生成一个单一的 SQL 语句来插入所有数据,并回填主键的值,钩子方法也会被调用。
varusers = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
DB.Create(&users)
3)通过Map数据类型创建记录
GORM 支持根据 map[string]interface{} 和 []map[string]interface{}{} 单个/批量创建记录
DB.Model(&User{}).Create(map[string]interface{}{
"Name": "jinzhu", "Age": 18,
})
DB.Model(&User{}).Create([]map[string]interface{}{
{"Name": "jinzhu_1", "Age": 18},
{"Name": "jinzhu_2", "Age": 20},
})
删除
1)删除一条记录:
删除一条记录时,删除对象需要指定主键,否则会触发 批量 Delete
(1)db.Delete(&email) // email对象的id要有正确的值
(2)带额外条件的删除:db.Where("name = ?", "jinzhu").Delete(&email)
2)根据主键删除
(1)db.Delete(&User{}, 10)
(2)db.Delete(&User{}, "10")
(3)db.Delete(&users, []int{1,2,3})
3)批量删除
(1)db.Where("email LIKE ?", "%jinzhu%").Delete(&Email{})
(2)db.Delete(&Email{}, "email LIKE ?", "%jinzhu%")
4)返回删除行的数据
(1)返回所有列
var
users []User // 用来接收的
DB.Clauses(clause.Returning{}).Where("role = ?", "admin").Delete(&users)
(2)返回指定的列
DB.Clauses(clause.Returning{Columns:
[]clause.Column{{Name: "name"}, {Name: "salary"}}}).Where("role = ?", "admin").Delete(&users)
// DELETE FROM `users` WHERE role = "admin" RETURNING `name`, `salary`
5)软删除
(1)如果模型包含了一个 gorm.deletedat 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!
(2)拥有软删除能力的模型调用 Delete 时,记录不会从数据库中被真正删除。
但 GORM 会将 DeletedAt 置为当前时间, 并且你不能再通过普通的查询方法找到该记录。
(3)查询被软删除的记录, 可以使用 Unscoped 找到被软删除的记录:
db.Unscoped().Where("age = 20").Find(&users)
修改
1)保存所有字段
Save 会保存所有的字段,即使字段是零值:db.Save(&user)
2)更新单列(如一行记录的某个字段)
(1)当使用 Update 更新单列时,需要有一些条件,否则将会引起错误 ErrMissingWhereClause 。
(2)当使用 Model 方法,并且值中有主键值时,主键将会被用于构建条件。
(3)条件更新:db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
(4)根据传入model中的主键值更新:db.Model(&user).Update("name", "hello") // 传入的user 的 ID 是 xxx
(5)根据条件和 model 的值进行更新:
3)更新多列
Updates 方法支持 struct 和 map[string]interface{} 参数。当使用 struct 更新时,默认情况下,GORM 只会更新非零值的字段
(1)根据struct:db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
(2)根据map:db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
4)更新选定字段
如果想要在更新时选定、忽略某些字段,您可以使用 Select、Omit
(1)选定字段
Select:db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// 语句为 UPDATE users SET name='hello' WHERE id=111;
(2)忽略字段 Omit:db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
(3)两者可以一起使用。
5)批量更新
如果尚未通过 Model 指定记录的主键,则 GORM 会执行批量更新,可以根据struct或map批量更新。
事务
禁用默认事务
为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,可以在初始化时禁用它提升性能。
1)全局禁用
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{ SkipDefaultTransaction: true, })2)持续会话模式
tx := db.Session(&Session{SkipDefaultTransaction: true}) tx.First(&user, 1) tx.Find(&users) tx.Model(&user).Update("Age", 18)
事务开启
要在事务中执行一系列操作,一般流程如下:
db.Transaction(func(tx *gorm.DB) error
{
// 在事务中执行一些
db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err !=
nil {
// 返回任何错误都会回滚事务
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err !=
nil {
return err
}
// 返回 nil 提交事务
return
nil
})
嵌套事务
GORM 支持嵌套事务,您可以回滚较大事务内执行的一部分操作:
db.Transaction(func(tx *gorm.DB) error {
tx.Create(&user1)
tx.Transaction(func(tx2 *gorm.DB) error {
tx2.Create(&user2)
return errors.New("rollback user2") // Rollback user2
})
return nil
})
手动事务
Gorm 支持直接调用事务控制方法(commit、rollback)
// 开始事务
tx := db.Begin()
// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
tx.Create(...)
// 遇到错误时回滚事务
tx.Rollback()
// 否则,提交事务
tx.Commit()