使用 GORM(Go 的 ORM 库)连接数据库,并实现增删改查操作
1.gorm的新增记录
1.1 插入数据
//定义一个用户,并初始化数据
u := User{
Username:"tizi365",
Password:"123456",
CreateTime:time.Now().Unix(),
}
//插入一条用户数据
//下面代码会自动生成SQL语句:INSERT INTO `users` (`username`,`password`,`createtime`) VALUES ('tizi365','123456','1540824823')
db.Create(&u)
//一般项目中我们会类似下面的写法,通过Error对象检测,插入数据有没有成功,如果没有错误那就是数据写入成功了。
if err := db.Create(&u).Error; err != nil {
fmt.Println("插入失败", err)
return
}
1.2 gorm获取新插入记录的自增Id
gorm 2.0版本以后的,默认会自动返回主键Id值。
实例代码:
//定义一个用户,并初始化数据
u := User{...忽略初始化代码...}
//插入记录
db.Create(&u)
u.ID // 返回主键id,默认主键名为ID,也可以通过gorm标签定义,请参考前面的模型定义章节
u.Error // 返回 error
u.RowsAffected // 返回插入记录的条数
提示:如果gorm设置了数据库连接池,那么每次执行数据库查询的时候都会从数据库连接池申请一个数据库连接,那么上述代码必须使用数据库事务,确保插入数据和查询自增id两条sql语句是在同一个数据库连接下执行,否则在高并发场景下,可能会查询不到自增id,或者查询到错误的id。
2.gorm查询数据
gorm查询数据本质上就是提供一组函数,帮我们快速拼接sql语句,尽量减少编写sql语句的工作量。
gorm查询结果我们一般都是保存到结构体(struct)变量,所以在执行查询操作之前需要根据自己想要查询的数据定义结构体类型。
提示:gorm库是协程安全的,gorm提供的函数可以并发的在多个协程安全的执行。
下面是教程用到的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.1 gorm链式操作函数查询数据
gorm查询主要由以下几个部分的函数组成,这些函数可以串起来组合sql语句,使用起来类似编写sql语句的习惯。
query
执行查询的函数,gorm提供下面几个查询函数:
- Take
查询一条记录
例子:
//定义接收查询结果的结构体变量
food := Food{}
//等价于:SELECT * FROM `foods` LIMIT 1
db.Take(&food)
- First
查询一条记录,根据主键ID排序(正序),返回第一条记录
例子:
//等价于:SELECT * FROM `foods` ORDER BY `foods`.`id` ASC LIMIT 1
db.First(&food)
- Last
查询一条记录, 根据主键ID排序(倒序),返回第一条记录
//等价于:SELECT * FROM `foods` ORDER BY `foods`.`id` DESC LIMIT 1
//语义上相当于返回最后一条记录
db.Last(&food)
- Find
查询多条记录,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表示有错误发生。
当 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)
}
where
上面的例子都没有指定where条件,这里介绍下如何设置where条件,主要通过db.Where函数设置条件.
函数说明:
db.Where(query interface{}, args ...interface{})
参数说明:
例子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)