Dawn's Blogs

分享技术 记录成长

0%

GO语言杂谈 (10) GORM

ORM

ORM(Object Relational Mapping,对象关系映射),作用是在关系型数据库和对象之间作一个映射。

1
2
3
数据表	 <-->  类
数据行 <--> 类的实例
字段 <--> 类的字段

GROM入门

详细内容查看GORM文档

连接数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

type UserInfo struct {
ID uint
Name string
Gender string
Hobby string
}

func main() {
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}

// 创建表 自动迁移(把结构体与数据表进行对应)
db.AutoMigrate(&UserInfo{})

// 插入数据
u1 := UserInfo{1, "Dawn", "男", "Study"}
db.Create(&u1)

// 查询
var res UserInfo
db.First(&res)
fmt.Println(res)

// 更新
db.Model(&res).Update("hobby", "Games")

// 删除
db.Delete(&res)
}

Grom.Model

为了方便模型定义,GORM内置了一个grom.Model结构体,其包括字段 IDCreatedAtUpdatedAtDeletedAt

1
2
3
4
5
6
7
// gorm.Model 的定义
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}

可以将它嵌入到自己定义的结构体中,以包含这几个字段:

1
2
3
4
5
6
7
8
9
10
11
12
// 定义模型
type User struct {
gorm.Model
Name string
Age sql.NullInt64 // 零值
Birthday *time.Time
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"` // 设置字段大小为255
MemberNumber *string `gorm:"unique;not null"` // 设置会员号(member number)唯一并且不为空
Address string `gorm:"index:addr"` // 给address字段创建名为addr的索引
IgnoreMe int `gorm:"-"` // 忽略本字段
}

字段标签

声明 model 时,tag 是可选的,GORM 支持以下 tag:

标签名 说明
column 指定 db 列名
type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT
size 指定列大小,例如:size:256
primaryKey 指定列为主键
unique 指定列为唯一
default 指定列的默认值
precision 指定列的精度
scale 指定列大小
not null 指定列为 NOT NULL
autoIncrement 指定列为自动增长
autoIncrementIncrement 自动步长,控制连续记录之间的间隔
embedded 嵌套字段
embeddedPrefix 嵌入字段的列名前缀
autoCreateTime 创建时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano
autoUpdateTime 创建/更新时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli
index 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情
uniqueIndex index 相同,但创建的是唯一索引
check 创建检查约束,例如 check:age > 13,查看 约束 获取详情
<- 设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限
-> 设置字段读的权限,->:false 无读权限
- 忽略该字段,- 无读写权限
comment 迁移时为字段添加注释

关联标签:

标签 描述
foreignKey 指定当前模型的列作为连接表的外键
references 指定引用表的列名,其将被映射为连接表外键
polymorphic 指定多态类型,比如模型名
polymorphicValue 指定多态值、默认表名
many2many 指定连接表表名
joinForeignKey 指定连接表的外键列名,其将被映射到当前表
joinReferences 指定连接表的外键列名,其将被映射到引用表
constraint 关系约束,例如:OnUpdateOnDelete

主键、表名、列名的约定

主键

GORM默认会使用名为ID的字段作为表的主键

1
2
3
4
type User struct {
ID string // 名为`ID`的字段会默认作为表的主键
Name string
}

表名

表名默认是结构体的复数

1
2
3
4
5
6
type User struct {} // 默认表名是 `users`

// 将 User 的表名设置为 `admin_users`
func (User) TableName() string {
return "admin_users"
}

可以通过Table()指定表名

1
2
3
4
5
6
7
8
9
// 使用User结构体创建名为`deleted_users`的表
db.Table("deleted_users").CreateTable(&User{})

var deleted_users []User
db.Table("deleted_users").Find(&deleted_users)
//// SELECT * FROM deleted_users;

db.Table("deleted_users").Where("name = ?", "jinzhu").Delete()
//// DELETE FROM deleted_users WHERE name = 'jinzhu';

列名

列名默认由字段名称进行下划线分割来生成:

1
2
3
4
5
6
type User struct {
ID uint // column name is `id`
Name string // column name is `name`
Birthday time.Time // column name is `birthday`
CreatedAt time.Time // column name is `created_at`
}

可以使用结构体tag指定列名:

1
2
3
4
5
type Animal struct {
AnimalId int64 `gorm:"column:beast_id"` // set column name to `beast_id`
Birthday time.Time `gorm:"column:day_of_the_beast"` // set column name to `day_of_the_beast`
Age int64 `gorm:"column:age_of_the_beast"` // set column name to `age_of_the_beast`
}

时间戳跟踪

  • 如果模型有 CreatedAt字段,该字段的值将会是初次创建记录的时间

  • 如果模型有UpdatedAt字段,该字段的值将会是每次更新记录的时间

  • 如果模型有DeletedAt字段,调用Delete删除该记录时,将会设置DeletedAt字段为当前时间,而不是直接将记录从数据库中删除