第六届字节跳动青训营——GORM学习之路第二节 | 青训营

73 阅读3分钟

内容概述

本文主要记录一下有关GORM的进阶内容——高级查询和事务操作。

主要内容

使用where查询

查询用户名是zhouzhou

DB.Where("name = ?", "zhouzhou").Find(&users)

查询用户名不是zhouzhou

DB.Where("name <> ?", "zhouzhou").Find(&users)

查询用户名包含zhouzhou,baobao

DB.Where("name in ?", []string{"zhouzhou","baobao"}).Find(&users)

查询姓周的

DB.Where("name like ?", "李%").Find(&users)

查询年龄大于24且是163邮箱

DB.Where("age > ? and email like ?", "24","%@163.com").Find(&users)

查询是女性或者年龄大于24

DB.Where("age > ? and gender = ?", "24", false).Find(&users)

使用结构体查询

使用结构体查询,会过滤零值。并且结构体中的条件都是and关系

DB.Where(&Student{Name: "zhouzhou", Age: 0}).Find(&users)

使用map查询

使用map查询,不会过滤零值

DB.Where(map[string]any{"name": "zhouzhou", "age": 0}).Find(&users)

使用Not查询

排除年龄age大于23岁的

DB.Not("age > 23").Find(&users)

使用or查询

查询年龄等于23岁的,或者邮箱是163邮箱的

DB.Or("age = ?", 23).Or(" email like ?", "%@163.com").Find(&users)

使用select查询

选择name和age两列

DB.Select("name", "age").Find(&users)

使用Order排序

desc表示降序,asc表示升序。根据年龄倒序

DB.Order("age desc").Find(&users)

使用distinct去除重复

去除年龄相同的

DB.Table("students").Select("age").Distinct("age").Scan(&ageList)
DB.Table("students").Select("distinct age").Scan(&ageList)

分组查询

根据性别分组查询

DB.Table("students").Select("count(id)").Group("gender").Scan(&ageList)

子查询

查询大于平均年龄的

DB.Model(Student{}).Where("age > (?)", DB.Model(Student{}).Select("avg(age)")).Find(&users)

事务

事务就是用户定义的一系列数据库操作,这些操作可以视为一个完整的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。

为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,可以在初始化时禁用它,这将获得大约 30%+ 性能提升。

很形象的一个例子,zhouzhou给baobao转账520元,在程序里面,zhouzhou的余额就要减去520,baobao的余额就要增加520。整个事件具有原子性,是一个整体,哪一步错了,整个事件都是失败的。

以zhouzhou给baobao转账为例,不使用事务的后果。zhouzhou给baobao转账520元,先给zhouzhou减去520,再给baobao增加520

var zhouzhou, baobao User
DB.Take(&zhouzhou, "name = ?", "周周")
DB.Take(&baobao, "name = ?", "包包")
zhouzhou.Money -= 520
DB.Model(&zhouzhou).Update("money", zhouzhou.Money)
baobao.Money += 520
DB.Model(&baobao).Update("money", baobao.Money)

在失败的情况下,假如两者有一方没有成功执行,那么张三白白损失了100,那么李四凭空拿到100元。这显然是不合逻辑的,并且不合法的。 那么,使用事务是怎样的

var zhouzhou, baobao User
DB.Take(&zhouzhou, "name = ?", "周周")
DB.Take(&baobao, "name = ?", "包包")
DB.Transaction(func(tx *gorm.DB) error {
  zhouzhou.Money -= 520
  err := tx.Model(&zhouzhou).Update("money", zhouzhou.Money).Error
  if err != nil {
    fmt.Println(err)
    return err
  }
  baobao.Money += 520
  err = tx.Model(&baobao).Update("money", baobao.Money).Error
  if err != nil {
    fmt.Println(err)
    return err
  }
  return nil
})

当然也提供手动事务。开始事务——在事务中执行一些 db 操作——遇到错误时回滚事务——否则,提交事务

tx := db.Begin()
tx.Create(...)
tx.Rollback()
tx.Commit()

以手动事务为例,对上面例子进行修改,如下:zhouzhou给baobao转账520元,先给zhouzhou减去520,再给baobao增加520,最后提交事务

var zhouzhou, baobao User
DB.Take(&zhouzhou, "name = ?", "周周")
DB.Take(&baobao, "name = ?", "包包")

// zhouzhou给baobao转账520元
tx := DB.Begin()

// 先给zhouzhou减去520
zhouzhou.Money -= 520
err := tx.Model(&zhouzhou).Update("money", zhouzhou.Money).Error
if err != nil {
  tx.Rollback()
}

// 再给baobao增加520
baobao.Money += 520
err = tx.Model(&baobao).Update("money", baobao.Money).Error
if err != nil {
  tx.Rollback()
}
// 提交事务
tx.Commit()

总结

本文主要记录了下GORM的高阶查询和事务操作。高阶查询的where,order,select,or等等,都是mysql相通的,这点很容易上手。事务操作则是为了保持数据一致性,十分重要。