GORM
gorm是Golang语言中一款性能极好的ORM库,对开发人员相对是比较友好的。它提供了强大的功能和简洁的 API,让数据库操作变得更加简单和易维护。
文档
中文 https://gorm.io/zh_CN/docs/index.html
安装
1 | go get -u gorm.io/gorm |
连接
https://gorm.io/zh_CN/docs/connecting_to_the_database.html#MySQL
1 | package main |
模型定义
https://gorm.io/zh_CN/docs/models.html
GORM 倾向于约定优于配置,如果不遵从约定就要写自定义配置
- 使用名为ID的属性会作为主键
- 使用snake_cases作为表名
- 结构体命名为employee,那么数据库表名就是employees
- 使用snake_case作为字段名,字段首字母大写
1 | // 不符合约定的定义,很多都需要配置,直接用不行 |
表名配置
1 | // 表名并没有遵守约定 |
字段配置
1 | package main |
使用 gorm:"primaryKey"
来指定字段为主键,默认使用名为ID的属性作为主键。primaryKey是tag名,大小写不敏感,但建议小驼峰。
列名
如果未按照约定定义字段,需要定义结构体属性时指定数据库字段名称是什么。
1 | BirthDate string `gorm:"column:birth_date"` // 字段名可以不符合约定,但字段名首字母一定要大写 |
GORM常用操作
https://gorm.io/zh_CN/docs/migration.html#%E8%A1%A8
下面,新建一个students表,来看看结构体中属性类型和数据库表中字段类型的对应关系
1 | var db *gorm.DB |
由于int => bigint、string => longtext,这些默认转换不符合我们的要求,所以,在tag中使用type指定字段类型。
属性是用来构建结构体实例的,生成的SQL语句也要使用这些数据。而tag是用来生成迁移
1 | Name string `gorm:"size:48"` // 定义为varchar(48) |
迁移用的较少,主要是理解其作用。
结构体属性类型用来封装实例的数据,Tag中类型指定迁移到数据库表中字段的类型
新增记录
参考 https://gorm.io/zh_CN/docs/create.html#%E5%88%9B%E5%BB%BA%E8%AE%B0%E5%BD%95
1 | package main |
查询一条记录
Take被转换为Limit 1
1 | func main() { |
1 | row := db.First(&s) // ORDER BY `students`.`id` LIMIT 1 |
根据id查,可以使用下面的方式
1 | row := db.First(&s, 15) |
时间相关错误
1、时间类型字段
上例错误如下,主要是使用了*time.Time,而不是string。
1 | sql: Scan error on column index 3, name "birthday": unsupported Scan, storing driver.Value type []uint8 into type *time.Time |
解决方案
在连接字符串中增加parseTime=true,这样时间类型就会自动转化为time.Time类型
1 | dsn := "root:123456@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=true" |
也可以 Birthday string ,拿到Birthday字符串后,必要时,自行转换成时间类型
2、UTC时间
Create写入的时间,也就是说time.Now()取当前时区时间,但是存入数据库的时间是UTC时间。
Take拿回的时间也是UTC时间,可以通过s.Birthday.Local()转成当前时区时间。
如果想存入的时间或读取的时间直接是当前时区时间,可以使用loc参数loc=Local。
如果loc=Local
- 存入时,数据库字段中的时间就是当前时区的时间值
- 读取时,数据库字段中的时间就被解读为当前时区
1 | dsn := "root:123456@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=true&loc=Local" |
千万不要存入数据库时采用Local存入,却使用UTC解读时间,会造成时间时区的混乱。应该保证时间存入、读取时区一致。
一定要统一项目中数据库中时间类型字段的时区。可以考虑统一采用UTC,为了本地化显示转换为当前时区即可。
查询所有数据
1 | var students []*Student |
distinct
1 | rows := db.Distinct("name").Find(&students) |
投影
投影是关系模型的操作,就是选择哪些字段
1 | rows := db.Select("id", "name", "age").Find(&students) |
Limit和Offset
1 | var students []*Student |
条件查询
1、字符串条件
1 | var students []*Student |
2、struct 或map 条件
1 | var students []*Student |
3、Not
1 | var students []*Student |
4、Or
Or的用法和Where一样,Where().Where()是And关系,Where().Or()是Or关系
1 | var students []*Student |
排序
1 | var students []*Student |
分组
1 | var students []*Student |
可以使用以下方式保存count的值
1 | // 使用Scan填充容器 |
1 | // 使用Rows()返回所有行,自行获取字段值,但是要用Table指定表名 |
Join
1 | // Join |
更新
https://gorm.io/zh_CN/docs/update.html
先查后改:先查到一个实例,对这个实例属性进行修改,然后调用db.Save()方法保存。
db.Save()方法会保存所有字段,对于没有主键的实例相当于Insert into,有主键的实例相当于Update。
1 | // 更新 |
Update 更新单个字段
1 | // Update 更新单个字段 |
Updates 更新多列
1 | // Updates 更新多个字段 |
删除
https://gorm.io/zh_CN/docs/delete.html
删除操作是危险的,慎重操作!
1 | // 删除 |