tips:

关联自动创建,更新

关联模式

Association()

要启动关联模式,请指定源模型关系的字段名称

源模型必须包含主键,并且关系的字段名称应与现有关联匹配。

1
2
Db.Model(&User).Association(&Teacher)...// delete,replace,append...
// Model-源模型 Teacher是关系的字段

关联标签

标签 描述
foreignKey 指定在联接表中用作外键的当前模型的列名。
references 指示连接表的外键映射到的引用表中的列名。
polymorphic 定义多态类型,通常为模型名称。
polymorphicValue 设置多态值,通常为表名(如果未另行指定)。
many2many 命名多对多关系中使用的联接表。
joinForeignKey 标识联接表中映射回当前模型表的外键列。
joinReferences 指向联接表中链接到引用模型表的外键列。
constraint 为关联指定关系约束,如 。OnUpdate``OnDelete

Belong to

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Belongs to
// 迁移的时候只需要传入一个Student结构体
type Student struct {
gorm.Model
Name string
// belongs to
TeacherID uint
Teacher Teacher
}

type Teacher struct {
gorm.Model
Name string
}
// 只需要添加学生结构体
db.AutoMigrate(&Student)

Has Many

实例中包含着另一个实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Has one
// 迁移的时候两个结构体都需要传入
type Student1 struct {
gorm.Model
Name string
// has one
TeacherID uint
}

type Teacher1 struct {
gorm.Model
Name string
// has one
Student Student1
}

func One2One() {
Db.AutoMigrate(&Student1{})
}

预加载

如果结构体A 包含了 结构体B

并且两者确立了关系,就可以用预加载的方法进行查询

并且是可以 A预加载B;B预加载A

1
2
3
func change(){
Db.ProLoad("Dog").First(&Dog)
}

关联关系

1
2
3
4
Db.Model(&User).Association("Dog").Replace(&Dog{xxx})
Db.Model(&User).Association("Dog").Replace(&Dog{xxx})
Db.Model(&User).Association("Dog").Clear()
Db.Model(&User).Association("Dog").Delete(&Dog{xxx})

区别

Belongs to 中结构体A拥有结构体B的所有信息,而结构体B却不知道结构体A的信息

Has Many中结构体A和结构体B中信息是互相的,但是有一个主动的一方

但事实上,我拥有它和它属于我只是一个正向逆向的过程,都能用

Gorm中是自动关联,不用管外键,除非自定义

一般外键就是NameID


Has Many

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// User 有多张 CreditCard,UserID 是外键
// 准确来说就是Has One中改了个切片,毕竟需要一对多
// 迁移的时候也需要同时加入两个结构体
type User struct {
gorm.Model
CreditCards []CreditCard
}

type CreditCard struct {
gorm.Model
Number string
UserID uint
}

func Migrate(){
Db.AutoMigrate(&CrediCard,&User)
}

func Create(){
Db.Create(&User)
// 不用再重复添加一遍CreditCard
}

带条件的预加载

一对多关系如果要获得一对一关系就需要使用一些条件来限定范围

1
2
3
4
5
6
7
8
// 通过内联条件查询
Db.Preload("Dog", "name = ?", "ah").Find(&Girl{})

// 自定义函数的内联条件查询
Db.Preload("Dog", func(db *gorm.DB)*gorm.DB{
return db.Where("name = ?", "dog1")
}).Find(&Girl{})

链式预加载 (易错)

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
// 表结构
type Info struct {
gorm.Model
Money int
DogID uint
}

type Dog struct {
gorm.Model
Name string
GirlID uint
Info Info
}

type Girl struct {
gorm.Model
Name string
Dog Dog
}

func Chains() {
Db.AutoMigrate(&Infor{})
Db.AutoMigrate(&Dog{})
Db.AutoMigrate(&Girl{})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 预加载
// 查询条件只适用于当前预加载的那一层

// 会显示出所有Dogs的信息 (其中Dogs2是属于不满足情况的一类)
Db.Preload("Dogs.Info","name = ?","xxx").Preload("Dogs").Find(&Girl{})
// print: {Dogs1,{0,0,0}} , {Dogs2,{0,0,0}}

------
// 会显示出name=xxx的狗的信息
Db.Preload("Dogs.Info","money > ?","xxx").Preload("Dogs","name = ?","xxx").Find(&Girl{})
// print: {Dogs1,{xx,xx,xx}} , {Dogs2,{xx,xx,xx}}
//总结
就是一层一层分,先分狗,再分Info
不能再Info那一层去分Dog
例如:
Db.Preload("Dogs.Info","name = ?","xxx").Find(&Girl{}) // 这也是不行的
Db.Preload("Dogs.Info","money = ?","xxx").Find(&Girl{}) // 这个是对的,因为Info中包含money

每层的Preload预加载只能决定你当前层的数据能不能被带出来

但是,我们会发现无论如何查询,Preload的查询永远都是把所有结果都给查询出来(其实)

哪怕是没有在限定条件下的数据,可以从上面的print中可以看出,无论怎么设定条件

Dogs1 , Dogs2都会输出结果,没找到就是在后面输出{0,0,0},而不是我们想要的不显示我们不需要的数据

那么如何 能够不显示 我没有找到的数据呐

NX有结果显示,更加清晰理解

JOINS 预加载

1
2
3
4
5
DB.GLOBAL_DB.Preload("Student", func(db *gorm.DB) *gorm.DB {
return db.Joins("Info").Where("score > ?", 60)
}).First(&result)
// print {Stu1 {xx , xx , xx}}
// 注意JOINS的预加载不能使用内联的条件判断

在这个例子中就不会输出我们不需要的内容

重新梳理

下面我们看官网的案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type User struct {
gorm.Model
Username string
Orders []Order
}

type Order struct {
gorm.Model
UserID uint
Price float64
}

// 查找 user 时预加载相关 Order
db.Preload("Orders").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4);

db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
// SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one
// SELECT * FROM roles WHERE id IN (4,5,6); // belongs to

可以发现 Preload的作用域在于 ID,其在预加载的是其他表的外键

而JOINS的使用则是直接加载一整张表

由此可以明白上述情况的出现状况


Many To Many

两张表的关系通过一个中间表来控制

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
// 创建实例
type Info struct {
gorm.Model
Money int
DogID uint
}

type Dog struct {
gorm.Model
Name string
Girls []*Girl `gorm:"many2many:dog_girl;"`
Info Info
}

type Girl struct {
gorm.Model
Name string
Dogs []*Dog `gorm:"many2many:dog_girl;"`
}

func Ha() {
Db.AutoMigrate(&Dog{}, &Girl{}, &Info{})
info := Info{
Money: 1200,
}
g1 := &Girl{
Model: gorm.Model{
ID: 1,
},
Name: "girl1",
}
g2 := &Girl{
Model: gorm.Model{
ID: 2,
},
Name: "girl2",
}
d := &Dog{
Model: gorm.Model{
ID: 3,
},
Info: info,
Name: "dog2",
Girls: []*Girl{g1, g2},
}
tx := Db.Create(d)
if tx.Error != nil {
log.Println(tx.Error)
}
}

简单的查询

1
2
var dog []Dog
Db.Preload("Girls").Find(&dog) // 查询dog的情况,同时可以查询到girl的状态

如果只需要Girls的切片

1
2
var dog []Dog
Db.Model(&Dog).Association("Girls").Find(&dog)//可以查询出带有Girls字段的值

如果想要把Girls中的Dog也显现出来

1
2
3
// 使用preload预加载
var dog []Dog
Db.Model(&Dog).Preload("dog").Association("Girls").Find(&dog)

也可以使用自定义语句等等………

维护中间表的关系

1
2
3
4
5
6
var d Dog
var g1, g2 Girl
Db.Model(&d).Association("Girl").Append( &g1 , &g2 )
Db.Model(&d).Association("Girl").Delete( &g1)
Db.Model(&d).Association("Girl").Replace( &g1)
Db.Model(&d).Association("Girl").Clear()