Gorm框架 查询数据
Gorm 提供了一组函数,用于快速拼接生成 Sql 语句,尽量减少编写 Sql 语句的工作量。同时,Gorm 将查询结果保存到结构体(struct)变量中,方便使用。所以,我们在执行查询操作之前,需要定义结构体类型,用于保存期望的查询结果。
Gorm 框架库是协程安全的,它提供的函数可以在多个协程中,安全地并发执行。
Gorm 查询主要由query,where,order,limit,count,group 等多个函数组成,这些函数可以串起来组合 Sql 语句,使用起来类似编写 Sql 语句的习惯。
1. 定义返回结果的结构体
下面是教程用到的 foods 表结构定义:
CREATE TABLE `foods` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品id', `title` varchar(100) NOT NULL COMMENT '商品名', `price` float DEFAULT '0' COMMENT '商品价格', `stock` int(11) DEFAULT '0' COMMENT '商品库存', `type` int(11) DEFAULT '0' COMMENT '商品类型', `create_time` datetime NOT NULL COMMENT '商品创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
下面是 foods 表对应的 golang 结构体类型
//商品 type Food struct { Id int Title string Price float32 Stock int Type int //mysql datetime, date类型字段,可以和golang time.Time类型绑定, 详细说明请参考:gorm连接数据库章节。 CreateTime time.Time } // 为Food绑定表名 func (v Food) TableName() string { return "foods" }
2. Query 查询方法
执行查询的函数,Gorm 提供下面几个查询函数:
1)Take
查询一条记录。
// 定义接收查询结果的结构体变量 food := Food{} // 等价于:SELECT * FROM `foods` LIMIT 1 db.Take(&food)
2)First
查询一条记录,根据主键ID排序(正序),返回第一条记录。
// 等价于:SELECT * FROM `foods` ORDER BY `foods`.`id` ASC LIMIT 1 db.First(&food)
3) Last
查询一条记录, 根据主键ID排序(倒序),返回第一条记录。
>// 等价于:SELECT * FROM `foods` ORDER BY `foods`.`id` DESC LIMIT 1 // 语义上相当于返回最后一条记录 db.Last(&food)
4) Find
查询多条记录,Find函数返回的是一个数组。
// 因为Find返回的是数组,所以定义一个商品数组用来接收结果 var foods []Food // 等价于:SELECT * FROM `foods` db.Find(&foods)
5) Pluck
查询一列值。
//商品标题数组 var titles []string // 返回所有商品标题 // 等价于:SELECT title FROM `foods` // Pluck提取了title字段,保存到titles变量 // 这里Model函数是为了绑定一个模型实例,可以从里面提取表名。 db.Model(&Food{}).Pluck("title", &titles)
3. 查询错误处理
通过 db.Error 属性判断查询结果是否出错, Error 属性不等于 nil 表示有错误发生。
if err := db.Take(&food).Error; err != nil { fmt.Println("查询失败", err) }
错误特例:
当 First、Last、Take 方法找不到记录时,Gorm 会返回 ErrRecordNotFound 错误。
在实际开发中查询不到数据,我们不一定会当成错误处理, Gorm 库通过下面办法检测 Error 是不是查询不到数据。
err := db.Take(&food).Error if errors.Is(err, gorm.ErrRecordNotFound) { fmt.Println("查询不到数据") } else if err != nil { // 如果err不等于record not found错误,又不等于nil,那说明sql执行失败了。 fmt.Println("查询失败", err) }
4. Where 方法
上面的例子都没有指定 where 条件,这里介绍下如何设置 where 条件,主要通过 db.Where 函数设置条件。
where 函数格式:
db.Where(query interface{}, args ...interface{})
参数说明:
- query:Sql 语句的 where 子句, where 子句中使用问号(?)代替参数值,则表示通过 args 参数绑定参数;
- args:where 子句绑定的参数,可以绑定多个参数。
// 例子1: // 等价于: SELECT * FROM `foods` WHERE (id = '10') LIMIT 1 // 这里问号(?), 在执行的时候会被 10 替代 db.Where("id = ?", 10).Take(&food) // 例子2: // in 语句 // 等价于: SELECT * FROM `foods` WHERE (id in ('1','2','5','6')) LIMIT 1 // args参数传递的是数组 db.Where("id in (?)", []int{1,2,5,6}).Take(&food) // 例子3: // 等价于: SELECT * FROM `foods` WHERE (create_time >= '2018-11-06 00:00:00' and create_time <= '2018-11-06 23:59:59') // 这里使用了两个问号(?)占位符,后面传递了两个参数替换两个问号。 db.Where("create_time >= ? and create_time <= ?", "2018-11-06 00:00:00", "2018-11-06 23:59:59").Find(&foods) // 例子4: // like语句 // 等价于: SELECT * FROM `foods` WHERE (title like '%可乐%') db.Where("title like ?", "%可乐%").Find(&foods)
设置 Select 子句, 指定返回的字段:
// 例子1: // 等价于: SELECT id,title FROM `foods` WHERE `foods`.`id` = '1' AND ((id = '1')) LIMIT 1 db.Select("id,title").Where("id = ?", 1).Take(&food) // 这种写法是直接往Select函数传递数组,数组元素代表需要选择的字段名 db.Select([]string{"id", "title"}).Where("id = ?", 1).Take(&food) // 例子2: // 可以直接书写聚合语句 // 等价于: SELECT count(*) as total FROM `foods` total := []int{} // Model函数,用于指定绑定的模型,这里生成了一个Food{}变量。目的是从模型变量里面提取表名,Pluck函数我们没有直接传递绑定表名的结构体变量,gorm库不知道表名是什么,所以这里需要指定表名 // Pluck函数,主要用于查询一列值 db.Model(&Food{}).Select("count(*) as total").Pluck("total", &total) fmt.Println(total[0])
5. Order 方法
设置排序语句,也就是 order by 子句。
// 例子: // 等价于: SELECT * FROM `foods` WHERE (create_time >= '2018-11-06 00:00:00') ORDER BY create_time desc db.Where("create_time >= ?", "2018-11-06 00:00:00").Order("create_time desc").Find(&foods)
6. Limit,Offset 方法
设置 limit 和 Offset 子句,分页的时候常用语句。
// 等价于: SELECT * FROM `foods` ORDER BY create_time desc LIMIT 10 OFFSET 0 db.Order("create_time desc").Limit(10).Offset(0).Find(&foods)
7. Count 方法
Count 函数,直接返回查询匹配的行数。
//例子: var total int64 = 0 // 等价于: SELECT count(*) FROM `foods` // 这里也需要通过model设置模型,让 gorm 可以提取模型对应的表名 db.Model(Food{}).Count(&total) fmt.Println(total)
8. Group 方法
设置 group by 子句。
// 例子: // 统计每个商品分类下面有多少个商品 // 定一个Result结构体类型,用来保存查询结果 type Result struct { Type int Total int } var results []Result // 等价于: SELECT type, count(*) as total FROM `foods` GROUP BY type HAVING (total > 0) db.Model(Food{}).Select("type, count(*) as total").Group("type").Having("total > 0").Scan(&results) // scan类似Find都是用于执行查询语句,然后把查询结果赋值给结构体变量,区别在于scan不会从传递进来的结构体变量提取表名. // 这里因为我们重新定义了一个结构体用于保存结果,但是这个结构体并没有绑定foods表,所以这里只能使用scan查询函数。
提示:Group函数必须搭配Select函数一起使用
9. 直接执行 Sql 语句
对于复杂的查询,例如多表连接查询,我们可以直接编写 Sql 语句,然后执行 Sql 语句。
Gorm 通过 db.Raw 设置 Sql 语句,通过 Scan 执行查询。
sql := "SELECT type, count(*) as total FROM `foods` where create_time > ? GROUP BY type HAVING (total > 0)" // 因为sql语句使用了一个问号(?)作为绑定参数, 所以需要传递一个绑定参数(Raw第二个参数). // Raw函数支持绑定多个参数 db.Raw(sql, "2018-11-06 00:00:00").Scan(&results) fmt.Println(results)
下一章:Gorm框架 更新数据
Gorm 更新记录 Save 方法:用于保存模型变量的值。Gorm 更新记录 Update 方法:更新单个字段值。Gorm 更新记录 Updates 方法:更新多个字段值。更新表达式:gorm 提供了 Expr 函数用于设置表达式。