gorm对象关系映射

什么是ORM

image-20221129230754556

ORM优缺点

  • 优点
    • 提高开发效率
  • 缺点
    • 牺牲执行性能
    • 牺牲灵活性
    • 弱化SQL能力

基础入门

下载依赖

1
2
3
4
5
6
7
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite

//或者
go get -u github.com/jinzhu/gorm
go get -u github.com/go-sql-driver/mysql
go mod tidy

导入包

1
2
3
4
import (
"gorm.io/gorm"
"gorm.io/driver/sqlite"
)

一个栗子

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
41
42
43
44
45
46
47
48
49
50
51
52
53
package main

import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)

// UserInfo 用户信息
type UserInfo struct {
ID uint
Name string
Gender string
Hobby string
}

func main() {
// 连接数据库
db, err := gorm.Open("mysql", "root:password@(127.0.0.1:3306)/web2?charset=utf8mb4&parseTime=True&loc=Local")
if err != nil {
panic(err)
}
defer db.Close()

// 自动迁移(相当于创建数据库)
db.AutoMigrate(&UserInfo{})

u1 := UserInfo{1, "贾小白", "男", "羽毛球"}
u2 := UserInfo{0, "梅西", "男", "足球"}
u3 := UserInfo{0, "c罗", "男", "足球"}
// 创建记录
db.Create(&u1)
db.Create(&u2)
db.Create(&u3)

// 查询第一个user
var u = new(UserInfo)
db.First(u)

fmt.Printf("第一个user是: %v\n", u)

var uu UserInfo
db.Find(&uu, "hobby=?", "足球")
// 查找hobby是足球的user
fmt.Printf("查找hobby是足球的user: %v\n", uu)

// 更新
db.Model(&u).Update("hobby", "蓝球")
fmt.Printf("修改后第一个user:%v\n", u)
// 删除
//db.Delete(&u)
}

image-20221129195847490

模型定义

gorm.Model

GORM 定义一个 gorm.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)唯一并且不为空
Num int `gorm:"AUTO_INCREMENT"` // 设置 num 为自增类型
Address string `gorm:"index:addr"` // 给address字段创建名为addr的索引
IgnoreMe int `gorm:"-"` // 忽略本字段
}

嵌入结构体

1
2
3
4
5
6
7
8
9
10
11
12
type User struct {
gorm.Model
Name string
}
// 等效于
type User struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string
}

字段标签

声明 model 时,tag 是可选的,GORM 支持以下 tag: tag 名大小写不敏感,但建议使用 camelCase 风格

标签名 说明
column 指定 db 列名
type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT
serializer 指定将数据序列化或反序列化到数据库中的序列化器, 例如: serializer:json/gob/unixtime
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 无读权限
- 忽略该字段,- 表示无读写,-:migration 表示无迁移权限,-:all 表示无读写迁移权限
comment 迁移时为字段添加注释

CRUD基础操作

查询

检索单个对象

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
package main

import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)

// UserInfo 用户信息
type UserInfo struct {
ID uint
Name string
Gender string
Hobby string
}

func main() {
// 连接数据库
db, err := gorm.Open("mysql", "root:password@(127.0.0.1:3306)/web2?charset=utf8mb4&parseTime=True&loc=Local")
if err != nil {
panic(err)
}
defer db.Close()

// 自动迁移(相当于创建数据库)
db.AutoMigrate(&UserInfo{})

// 查询第一个user
var u UserInfo
db.First(&u)
fmt.Printf("第一个user是: %v\n", u)

var u2 UserInfo
db.Last(&u2)
fmt.Printf("最后一个user是: %v\n", u2)

}

FirstLast 会根据主键排序,分别查询第一条和最后一条记录。

上面程序输出结果

image-20221129201847122

根据主键检索

如果主键是数字类型,您可以使用 内联条件 来检索对象。 传入字符串参数时,需要特别注意 SQL 注入问题。

1
2
3
4
5
6
7
8
db.First(&user, 10)
// SELECT * FROM users WHERE id = 10;

db.First(&user, "10")
// SELECT * FROM users WHERE id = 10;

db.Find(&users, []int{1,2,3})
// SELECT * FROM users WHERE id IN (1,2,3);

image-20221129202753373

如果主键是字符串(例如像 uuid),查询将被写成这样

1
2
3
db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";

检索全部对象

1
2
3
4
5
6
// Get all records
result := db.Find(&users)
// SELECT * FROM users;

result.RowsAffected // returns found records count, equals `len(users)`
result.Error // returns error

image-20221129202854180

条件

String 条件:

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
// Get first matched record
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;

// Get all matched records
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';

// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');

// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';

// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;

// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';

// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00'

更新

保存所有字段

1
2
3
4
5
6
// 保存所有字段
var u UserInfo
db.First(&u)
u.Name = "jpc"
db.Save(&u)
// UPDATE user_info SET name='jpc' WHERE id=1

image-20221129204246740

更新单个列

1
2
3
4
// 更新单个列
db.Model(&UserInfo{}).Where("name = ?", "jpc").Update("name", "贾小白")
// UPDATE user_info SET name='贾小白' WHERE active='jpc';

更新多列

1
2
3
// 更新多个列
db.Model(&UserInfo{}).Where("name = ?", "贾小白").Updates(map[string]interface{}{"name": "jpc", "hobby": "羽毛球"})
// UPDATE user_info SET name='jpc', hobby='羽毛球' WHERE name="贾小白"

删除

根据主键删除

1
2
3
4
5
6
7
8
9
db.Delete(&User{}, 10)
// DELETE FROM users WHERE id = 10;

db.Delete(&User{}, "10")
// DELETE FROM users WHERE id = 10;

db.Delete(&users, []int{1,2,3})
// DELETE FROM users WHERE id IN (1,2,3);