> gorm查询数据本质上就是提供一组函数,帮我们快速拼接sql语句,尽量减少编写sql语句的工作量
[TOC]
## 测试表
~~~
CREATE TABLE `food` (
`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;
~~~
## 模型定义
~~~
//商品
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 "food"
}
~~~
## 使用gorm链式操作函数查询数据
### query
**Take**
> 查询一条记录
~~~
food := Food{}
//等价于:SELECT * FROM `foods` LIMIT 1
db.Take(&food)
~~~
**Find**
> 查询多条记录,Find函数返回的是一个数组
~~~
var foods []Food
//等价于:SELECT * FROM `foods`
db.Find(&foods)
~~~
**Pluck**
> 查询一列值
~~~
//商品标题数组
var titles []string
//返回所有商品标题
//等价于:SELECT title FROM `foods`
//Pluck提取了title字段,保存到titles变量
//这里Model函数是为了绑定一个模型实例,可以从里面提取表名。
db.Model(Food{}).Pluck("title", &titles)
~~~
**查询错误处理**
> 通过db.Error属性判断查询结果是否出错, Error属性不等于nil表示有错误发生
~~~
if err := db.Take(&food).Error; err != nil {
fmt.Println("查询失败", err)
}
~~~
> 查询不到数据, gorm也会当成错误处理
~~~
err := db.Take(&food).Error
if gorm.IsRecordNotFoundError(err) {
fmt.Println("查询不到数据")
} else if err != nil {
//如果err不等于record not found错误,又不等于nil,那说明sql执行失败了。
fmt.Println("查询失败", err)
}
~~~
> 或者
~~~
if db.Take(&food).RecordNotFound() {
fmt.Println("查询不到数据")
}
~~~
### where
> 语法:`db.Where(query interface{}, args ...interface{})`
~~~
例子1:
//等价于: SELECT * FROM `foods` WHERE (id = '10') LIMIT 1
//这里问号(?), 在执行的时候会被10替代
db.Where("id = ?", 10).Take(&food)
//例子2:
//等价于: 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:
//等价于: SELECT * FROM `foods` WHERE (title like '%可乐%')
db.Where("title like ?", "%可乐%").Find(&foods)
~~~
### select
> 设置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])
~~~
### 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)
~~~
### 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)
~~~
### count
> Count函数,直接返回查询匹配的行数
~~~
//例子:
total := 0
//等价于: SELECT count(*) FROM `foods`
//这里也需要通过model设置模型,让gorm可以提取模型对应的表名
db.Model(Food{}).Count(&total)
fmt.Println(total)
~~~
### 分组
~~~
//例子:
//统计每个商品分类下面有多少个商品
//定一个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函数一起使用
### 直接执行sql语句
> 对于复杂的查询,例如多表连接查询,我们可以直接编写sql语句
~~~
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)
~~~
- 基础知识
- 开发环境
- 包名规则
- 包初始化 (init)
- 基础数据类型
- 基础类型转换
- 格式化输出
- go指针
- 流程控制语句
- 函数定义
- 匿名函数
- 数组和切片
- map集合
- 结构体
- Interface接口
- 日期处理
- 数学计算
- 正则表达式
- 协程 (并发处理)
- channel
- waitgroup
- mutex (锁机制)
- websocket
- protobuf
- Redis
- 错误处理
- 打包程序
- NSQ消息队列
- 单元测试
- beego
- 安装入门
- Gin
- 快速入门
- 路由与控制器
- 处理请求参数
- 表单验证
- 处理响应结果
- 渲染HTML模版
- 访问静态文件
- Gin中间件
- Cookie处理
- Session处理
- Gin上传文件
- swagger
- pprof性能测试
- GORM
- 入门教程
- 模型定义
- 数据库连接
- 插入数据
- 查询数据
- 更新数据
- 删除数据
- 事务处理
- 关联查询
- 属于 (BELONG TO)
- 一对一 (Has One)
- 一对多 (Has Many)
- 多对多 (Many to Many)
- 预加载 (Preloading)
- 错误处理
- 第三方常用插件
- viper 读取配置文件
- zap 高性能日志
- Nginx代理配置
- Goland 快捷键