使用gorm进行联合查询的整理总结
# 1,前言
任何学习都是循序渐进的,以往工作中,数据库相关的工作基本上都由 DBA 同学搞定了,于是自己对 db 这块儿就相当薄弱,最近在写 CRUD 的小项目时,不得不与之打交道了,这两天断续整理了一下利用 gorm 进行关联查询的一些小经验,特此整理总结,不敢怠慢。
前边写了用户管理的增删改查之后,来到了用户组这个环节,然而就卡住了,因为涉及到把用户加入到一个组内,一个组内可能有许多个用户,这样的场景该如何来实现呢?
看网上一些文章的思路大概如下,在用户的结构体当中直接把分组加进来,然后通过 Preload 等方式进行关联查询,示例代码大概如下:
type Group struct {
ID int `json:"id" gorm:"primary_key"`
GroupName string `json:"group_name"`
}
type User struct {
ID int `json:"id" gorm:"primary_key"`
Username string `json:"user_name"`
Group
}
2
3
4
5
6
7
8
9
这种思路也可以,不过在阅读了几个开源项目的思路,以及与同事进行了一些交流之后,发现大概不必这么麻烦,直接通过一个 ID 来进行关联,查询的时候使用 join 就行了。
# 2,实践
因为是实验所用,因此也就没有进行目录分配之类的工作,直接把所有的操作都放在 main 包当中了,这里先把代码放过来,稍后对一些值得分享的点进行讲解。
package main
import (
"fmt"
"time"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
var db *gorm.DB
// BaseModel 基础模型
type BaseModel struct {
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt *time.Time `sql:"index" json:"deletedAt"`
}
// User 用户模型
type User struct {
BaseModel
UserID int `json:"userId" gorm:"primary_key"`
NickName string `gorm:"type:varchar(128)" json:"nickName"` // 昵称
Phone string `gorm:"type:varchar(11)" json:"phone"` // 手机号
RoleID int `gorm:"type:int(11)" json:"roleId"` // 角色编码
Avatar string `gorm:"type:varchar(255)" json:"avatar"` //头像
Sex string `gorm:"type:varchar(255)" json:"sex"` //性别
Email string `gorm:"type:varchar(128)" json:"email"` //邮箱
DeptID int `gorm:"type:int(11)" json:"deptId"` //部门编码
CreateBy string `gorm:"type:varchar(128)" json:"createBy"` //
UpdateBy string `gorm:"type:varchar(128)" json:"updateBy"` //
Remark string `gorm:"type:varchar(255)" json:"remark"` //备注
Status string `gorm:"type:int(1);" json:"status"`
}
// TableName 指定用户表名称
func (User) TableName() string {
return "user"
}
// Dept 部门模型
type Dept struct {
BaseModel
DeptID int `json:"deptId" gorm:"primary_key;AUTO_INCREMENT"` //部门编码
ParentID int `json:"parentId" gorm:"type:int(11);"` //上级部门
DeptPath string `json:"deptPath" gorm:"type:varchar(255);"` //部门路径(以ID为标识)
DeptName string `json:"deptName" gorm:"type:varchar(128);"` //部门名称
Sort int `json:"sort" gorm:"type:int(4);"` //排序
Leader string `json:"leader" gorm:"type:varchar(128);"` //负责人
Phone string `json:"phone" gorm:"type:varchar(11);"` //手机
Email string `json:"email" gorm:"type:varchar(64);"` //邮箱
Status string `json:"status" gorm:"type:int(1);"` //状态
CreateBy string `json:"createBy" gorm:"type:varchar(64);"`
UpdateBy string `json:"updateBy" gorm:"type:varchar(64);"`
}
// TableName 指定部门表名称
func (Dept) TableName() string {
return "dept"
}
// Role 角色模型
type Role struct {
BaseModel
RoleID int `json:"roleId" gorm:"primary_key;AUTO_INCREMENT"` // 角色编码
RoleName string `json:"roleName" gorm:"type:varchar(128);"` // 角色名称
Status string `json:"status" gorm:"type:int(1);"` //
RoleKey string `json:"roleKey" gorm:"type:varchar(128);"` //角色代码
RoleSort int `json:"roleSort" gorm:"type:int(4);"` //角色排序
Flag string `json:"flag" gorm:"type:varchar(128);"` //
CreateBy string `json:"createBy" gorm:"type:varchar(128);"` //
UpdateBy string `json:"updateBy" gorm:"type:varchar(128);"` //
Remark string `json:"remark" gorm:"type:varchar(255);"` //备注
Admin bool `json:"admin" gorm:"type:char(1);"`
DataScope string `json:"dataScope" gorm:"type:varchar(128);"`
}
// TableName 指定角色表名称
func (Role) TableName() string {
return "role"
}
// InitDB 初始化DB
func InitDB() {
var err error
db, err = gorm.Open("mysql", "root:Passw0rd!@(192.168.0.1:3306)/dbtest?charset=utf8mb4&parseTime=True&loc=Local")
if err != nil {
panic(err)
}
// defer db.Close()
// 默认情况下,gorm创建的表将会是结构体名称的复数形式,如果不想让它自动复数,可以加一下禁用
db.SingularTable(true)
// 2, 把模型与数据库中的表对应起来
db.AutoMigrate(&User{})
db.AutoMigrate(&Dept{})
db.AutoMigrate(&Role{})
}
// CreateDept 创建部门
func CreateDept() {
Dept1 := &Dept{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, DeptID: 1, ParentID: 0, DeptPath: "/0/1", DeptName: "二丫讲梵科技", Sort: 0, Leader: "zhangsan", Phone: "15638888888", Email: "zhangsan@eryajf.net", Status: "1", CreateBy: "", UpdateBy: ""}
Dept2 := &Dept{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, DeptID: 2, ParentID: 1, DeptPath: "/0/1/2", DeptName: "技术部", Sort: 0, Leader: "zhangsan1", Phone: "15638888888", Email: "zhangsan1@eryajf.net", Status: "1", CreateBy: "", UpdateBy: ""}
Dept3 := &Dept{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, DeptID: 3, ParentID: 1, DeptPath: "/0/1/3", DeptName: "客服部", Sort: 0, Leader: "zhangsan1", Phone: "15638888888", Email: "zhangsan1@eryajf.net", Status: "1", CreateBy: "", UpdateBy: ""}
Dept4 := &Dept{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, DeptID: 4, ParentID: 1, DeptPath: "/0/1/2/4", DeptName: "运维部", Sort: 0, Leader: "lisi", Phone: "15638888888", Email: "lisi@eryajf.net", Status: "1", CreateBy: "", UpdateBy: ""}
defer db.Close()
db.Debug().Model(&Dept{}).Create(&Dept1) // INSERT INTO `dept` (`created_at`,`updated_at`,`deleted_at`,`dept_id`,`parent_id`,`dept_path`,`dept_name`,`sort`,`leader`,`phone`,`email`,`status`,`create_by`,`update_by`) VALUES ('2020-06-15 15:57:23','2020-06-15 15:57:23',NULL,1,0,'/0/1','二丫讲梵科技',0,'zhangsan','15638888888','zhangsan@eryajf.net','1','','')
db.Debug().Model(&Dept{}).Create(&Dept2) // INSERT INTO `dept` (`created_at`,`updated_at`,`deleted_at`,`dept_id`,`parent_id`,`dept_path`,`dept_name`,`sort`,`leader`,`phone`,`email`,`status`,`create_by`,`update_by`) VALUES ('2020-06-15 15:57:23','2020-06-15 15:57:23',NULL,2,1,'/0/1/2','技术部',0,'zhangsan1','15638888888','zhangsan1@eryajf.net','1','','')
db.Debug().Model(&Dept{}).Create(&Dept3) // INSERT INTO `dept` (`created_at`,`updated_at`,`deleted_at`,`dept_id`,`parent_id`,`dept_path`,`dept_name`,`sort`,`leader`,`phone`,`email`,`status`,`create_by`,`update_by`) VALUES ('2020-06-15 15:57:23','2020-06-15 15:57:23',NULL,3,1,'/0/1/3','客服部',0,'zhangsan1','15638888888','zhangsan1@eryajf.net','1','','')
db.Debug().Model(&Dept{}).Create(&Dept4) // INSERT INTO `dept` (`created_at`,`updated_at`,`deleted_at`,`dept_id`,`parent_id`,`dept_path`,`dept_name`,`sort`,`leader`,`phone`,`email`,`status`,`create_by`,`update_by`) VALUES ('2020-06-15 15:57:23','2020-06-15 15:57:23',NULL,4,1,'/0/1/2/4','运维部',0,'lisi','15638888888','lisi@eryajf.net','1','','')
}
// CreateRole 创建角色
func CreateRole() {
Role1 := &Role{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, RoleID: 1, RoleName: "系统管理员", Status: "1", RoleKey: "admin", RoleSort: 1, Flag: "", CreateBy: "", UpdateBy: "", Remark: "", Admin: true, DataScope: "1"}
Role2 := &Role{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, RoleID: 2, RoleName: "普通角色", Status: "1", RoleKey: "user", RoleSort: 1, Flag: "", CreateBy: "", UpdateBy: "", Remark: "", Admin: false, DataScope: "2"}
Role3 := &Role{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, RoleID: 3, RoleName: "测试角色", Status: "1", RoleKey: "test", RoleSort: 1, Flag: "", CreateBy: "", UpdateBy: "", Remark: "", Admin: false, DataScope: "3"}
defer db.Close()
db.Debug().Model(&Role{}).Create(&Role1) // INSERT INTO `role` (`created_at`,`updated_at`,`deleted_at`,`role_id`,`role_name`,`status`,`role_key`,`role_sort`,`flag`,`create_by`,`update_by`,`remark`,`admin`,`data_scope`) VALUES ('2020-06-15 16:02:17','2020-06-15 16:02:17',NULL,1,'系统管理员','1','admin',1,'','','','',true,'1')
db.Debug().Model(&Role{}).Create(&Role2) // INSERT INTO `role` (`created_at`,`updated_at`,`deleted_at`,`role_id`,`role_name`,`status`,`role_key`,`role_sort`,`flag`,`create_by`,`update_by`,`remark`,`admin`,`data_scope`) VALUES ('2020-06-15 16:02:17','2020-06-15 16:02:17',NULL,2,'普通角色','1','user',1,'','','','',false,'2')
db.Debug().Model(&Role{}).Create(&Role3) // INSERT INTO `role` (`created_at`,`updated_at`,`deleted_at`,`role_id`,`role_name`,`status`,`role_key`,`role_sort`,`flag`,`create_by`,`update_by`,`remark`,`admin`,`data_scope`) VALUES ('2020-06-15 16:02:17','2020-06-15 16:02:17',NULL,3,'测试角色','1','test',1,'','','','',false,'3')
}
// CreateUser 创建用户
func CreateUser() {
User1 := &User{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, NickName: "liql", Phone: "15638888888", RoleID: 1, Avatar: "", Sex: "man", Email: "liql@qq.com", DeptID: 4, CreateBy: "", UpdateBy: "", Remark: "", Status: "1"}
User2 := &User{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, NickName: "liql1", Phone: "15638888888", RoleID: 1, Avatar: "", Sex: "man", Email: "liql@qq.com", DeptID: 2, CreateBy: "", UpdateBy: "", Remark: "", Status: "1"}
User3 := &User{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, NickName: "liuzb", Phone: "15638888888", RoleID: 2, Avatar: "", Sex: "man", Email: "liql@qq.com", DeptID: 3, CreateBy: "", UpdateBy: "", Remark: "", Status: "1"}
User4 := &User{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, NickName: "liuzb1", Phone: "15638888888", RoleID: 2, Avatar: "", Sex: "man", Email: "liql@qq.com", DeptID: 4, CreateBy: "", UpdateBy: "", Remark: "", Status: "1"}
User5 := &User{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, NickName: "jiangby", Phone: "15638888888", RoleID: 3, Avatar: "", Sex: "man", Email: "liql@qq.com", DeptID: 2, CreateBy: "", UpdateBy: "", Remark: "", Status: "1"}
User6 := &User{BaseModel: BaseModel{CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: nil}, NickName: "jiangby1", Phone: "15638888888", RoleID: 3, Avatar: "", Sex: "man", Email: "liql@qq.com", DeptID: 3, CreateBy: "", UpdateBy: "", Remark: "", Status: "1"}
defer db.Close()
db.Debug().Model(&User{}).Create(&User1) // INSERT INTO `user` (`created_at`,`updated_at`,`deleted_at`,`nick_name`,`phone`,`role_id`,`avatar`,`sex`,`email`,`dept_id`,`create_by`,`update_by`,`remark`,`status`) VALUES ('2020-06-15 16:07:43','2020-06-15 16:07:43',NULL,'liql','15638888888',1,'','man','liql@qq.com',4,'','','','1')
db.Debug().Model(&User{}).Create(&User2)
db.Debug().Model(&User{}).Create(&User3)
db.Debug().Model(&User{}).Create(&User4)
db.Debug().Model(&User{}).Create(&User5)
db.Debug().Model(&User{}).Create(&User6) // INSERT INTO `user` (`created_at`,`updated_at`,`deleted_at`,`nick_name`,`phone`,`role_id`,`avatar`,`sex`,`email`,`dept_id`,`create_by`,`update_by`,`remark`,`status`) VALUES ('2020-06-15 16:07:43','2020-06-15 16:07:43',NULL,'jiangby1','15638888888',3,'','man','liql@qq.com',3,'','','','1')
}
// UserDept 用户以及部门联合查询============
type UserDept struct {
User
DeptName string `json:"deptName"`
}
// SelectUserDept 查看单个用户以及部门
func (x User) SelectUserDept(id int) ([]UserDept, error) {
var userdept []UserDept
if err := db.Debug().Table(x.TableName()).Select("user.*,dept.dept_name").Joins("left join dept on dept.dept_id=user.dept_id").Find(&userdept, id).Error; err != nil {
return nil, err
}
return userdept, nil
}
// SelectUserDepts 查看所有用户以及部门,当然还可以结合limit进行查询分页
func (x User) SelectUserDepts() ([]UserDept, error) {
var userdepts []UserDept
if err := db.Debug().Table(x.TableName()).Select("user.*,dept.dept_name").Joins("left join dept on dept.dept_id=user.dept_id").Find(&userdepts).Error; err != nil {
return nil, err
}
return userdepts, nil
}
// UserRole 用户以及角色联合查询============
type UserRole struct {
User
RoleName string `json:"roleName"`
}
// SelectUserRole 查看单个用户以及角色
func (x User) SelectUserRole(id int) ([]UserRole, error) {
var userrole []UserRole
if err := db.Debug().Table(x.TableName()).Select("user.*,role.role_name").Joins("left join role on role.role_id=user.role_id").Find(&userrole, id).Error; err != nil {
return nil, err
}
return userrole, nil
}
// SelectUserRoles 查看所有用户以及角色,当然还可以结合limit进行查询分页
func (x User) SelectUserRoles() ([]UserRole, error) {
var userroles []UserRole
if err := db.Debug().Table(x.TableName()).Select("user.*,role.role_name").Joins("left join role on role.role_id=user.role_id").Find(&userroles).Error; err != nil {
return nil, err
}
return userroles, nil
}
// UserView 用户以及所属角色和部门============
type UserView struct {
User
DeptName string `json:"deptName"`
RoleName string `json:"roleName"`
}
// SelectUserDeptRole 查询单个用户以及其所属角色和部门
func (x User) SelectUserDeptRole(id int) ([]UserView, error) {
var userview []UserView
if err := db.Debug().Table(x.TableName()).Select("user.*,dept.dept_name,role.role_name").Joins("left join dept on dept.dept_id=user.dept_id").Joins("left join role on role.role_id=user.role_id").Find(&userview, id).Error; err != nil {
return nil, err
}
return userview, nil
}
// SelectUserDeptRoles 查询多个用户以及其所属角色和部门
func (x User) SelectUserDeptRoles() ([]UserView, error) {
var userviews []UserView
if err := db.Debug().Table(x.TableName()).Select("user.*,dept.dept_name,role.role_name").Joins("left join dept on dept.dept_id=user.dept_id").Joins("left join role on role.role_id=user.role_id").Find(&userviews).Error; err != nil {
return nil, err
}
return userviews, nil
}
// DeptUser 部门下的用户============
type DeptUser struct {
Dept
NickName string `json:"nickName"`
}
// SelectDeptUser 查询某部门下所有用户
func (x Dept) SelectDeptUser(id int) ([]DeptUser, error) {
var deptuser []DeptUser
if err := db.Debug().Table(x.TableName()).Select("dept.*,user.nick_name").Joins("left join user on user.dept_id=dept.dept_id").Find(&deptuser, id).Error; err != nil {
return nil, err
}
return deptuser, nil
}
// RoleUser 角色下的用户============
type RoleUser struct {
Role
NickName string `json:"nickName"`
}
// SelectRoleUser 查询某个角色中的所有用户
func (x Role) SelectRoleUser(id int) ([]RoleUser, error) {
var roleuser []RoleUser
if err := db.Debug().Table(x.TableName()).Select("role.*,user.nick_name").Joins("left join user on user.role_id=role.role_id").Find(&roleuser, id).Error; err != nil {
return nil, err
}
return roleuser, nil
}
func main() {
// 1,初始化
InitDB()
defer db.Close()
// 2,创建
// CreateDept()
// CreateRole()
// CreateUser()
// 3,关联查询
// 查看单个用户以及部门
var s1 UserDept
a1, _ := s1.SelectUserDept(1)
fmt.Printf("用户ID为1的用户信息以及部门信息为:%v\n", a1)
a2, _ := s1.SelectUserDepts()
fmt.Printf("所有用户的信息以及部门信息为:%v\n", a2)
var s2 UserRole
b1, _ := s2.SelectUserRole(1)
fmt.Printf("用户ID为1的用户信息以及角色为:%v\n", b1)
b2, _ := s2.SelectUserRoles()
fmt.Printf("所有用户的信息以及角色为:%v\n", b2)
var s3 UserView
c1, _ := s3.SelectUserDeptRole(1)
fmt.Printf("用户ID为1的用户全部信息为:%v\n", c1)
c2, _ := s3.SelectUserDeptRoles()
fmt.Printf("所有用户的全部信息为:%v\n", c2)
var s4 DeptUser
d1, _ := s4.SelectDeptUser(2)
fmt.Printf("部门ID为2的用户有:%v\n", d1)
var s5 RoleUser
e1, _ := s5.SelectRoleUser(1)
fmt.Printf("角色ID为1的用户有:%v\n", e1)
}
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
代码简述:
如上代码首先声明了三个模型,然后通过三个模型创建了三张表,接着造了一些数据,然后从不同的维度对三张表进行关联查询。
虽然是个简单的验证学习程序,但是利用空闲时间全部自己手敲,不断踩坑摸索,也足足搞了两天,希望看到此处的你能在这段代码中获得些许启迪与帮助。
文章还没完,夏夜深深,先上一张美女图片清心,然后再进入技巧分享。
# 3,Tips
# 1,模型规划
事实上上边的用户模型在我看来应该还不是最终的样子,但是也隐约提供了一些思路,某些情况下,我们应该将这个大模型进行一定程度的拆分,把某些有关联性的或者可能会被独立应用的字段,进行单独的声明,然后再根据实际需求,将这些合并在一起组成真正的用户模型。
比如如下代码:
type UserLogin struct {
Username string `gorm:"type:varchar(64)" json:"username"`
Password string `gorm:"type:varchar(128)" json:"password"`
}
type User struct {
UserId int `gorm:"primary_key;AUTO_INCREMENT" json:"userId"`
NickName string `gorm:"type:varchar(128)" json:"nickName"` // 昵称
Phone string `gorm:"type:varchar(11)" json:"phone"` // 手机号
Avatar string `gorm:"type:varchar(255)" json:"avatar"` //头像
Sex string `gorm:"type:varchar(255)" json:"sex"` //性别
Email string `gorm:"type:varchar(128)" json:"email"` //邮箱
}
type SysUser struct {
UserLogin
User
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
这样的话 UserLogin
这个结构体就能够在写登陆的接口时进行应用了,从而不必再拿上一个大的用户结构体来完成登陆的功能,当然这里只是举个例子,实际开发中如何配置还应该根据实际来进行合理规划与设计。
# 2,表名汇总
在上边结构体紧接着的地方,我定义了如下函数:
// User 用户模型
type User struct {
BaseModel
UserID int `json:"userId" gorm:"primary_key"`
NickName string `gorm:"type:varchar(128)" json:"nickName"` // 昵称
Phone string `gorm:"type:varchar(11)" json:"phone"` // 手机号
RoleID int `gorm:"type:int(11)" json:"roleId"` // 角色编码
Avatar string `gorm:"type:varchar(255)" json:"avatar"` //头像
Sex string `gorm:"type:varchar(255)" json:"sex"` //性别
Email string `gorm:"type:varchar(128)" json:"email"` //邮箱
DeptID int `gorm:"type:int(11)" json:"deptId"` //部门编码
CreateBy string `gorm:"type:varchar(128)" json:"createBy"` //
UpdateBy string `gorm:"type:varchar(128)" json:"updateBy"` //
Remark string `gorm:"type:varchar(255)" json:"remark"` //备注
Status string `gorm:"type:int(1);" json:"status"`
}
// TableName 指定用户表名称
func (User) TableName() string {
return "user"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
这样一来,后续所有与用户表的操作都可以通过调用这个函数来进行,有点相当于提取公约数的感觉,或者把它理解为 hosts 解析到 DNS 的进化,这样做的好处是:
- 表名自由定制,我们可以在 return 的时候将表名指定成任意表名。
- 基于如上特性,后期如果表名变更等情况,不需要在代码各个地方进行更改了,而只需要更改这个地方即可。
# 3,包容关联
一开始我在进行关联查询的时候,因为没有针对关联查询的结果进行针对性接收,以至于在查询之后,总是得不到自己想要的结果,当时在代码中拿着用户的结构体来进行用户以及部门的关联查询,返回的时候总是没有部门信息,然而拿着 DEBUG 出来的 SQL 语句去 MySQL 中查询又可以查到,甚是懊恼。
现在摸索出来之后,似乎不怎么觉得这是个问题,然而因为前边对 go 语言掌握不透彻,以至于在这个问题上晕乎了一天。
用一句话来说明白这个问题:当我们要查询用户信息并且想得到这个用户是哪个部门的时候,单单使用 User 类型的变量,显然是接收不到部门信息的,即便语句上能够正确查询出来,但是接收者型号不对,因此也无法进行正确返回,因此进行这种查询时,应该先定义一个接收这个结果的类型才行,于是我把这个小节标题叫做包容关联,在关联的查询的时候,先创建一个包容你所想要的所有数据的对象。
type UserDept struct {
User
DeptName string `json:"deptName"`
}
2
3
4
然后再进行关联查询将结果赋给这个结构体,就能够看到程序返回自己想要的数据了。
暂时目前就想到了这些,关于 gorm 的查询语句,一开始也是不大会用,慢慢摸索试探,得到自己想要的结果即可,不必过于追求某些偏见之类的意识形态,在学习初期,我们首要任务是先把功能实现。
# 再思考
后来又一想,上边情景亦有局限,当我们从不同维度创建分组,然后把一个用户加入到多个组的时候,会发现一个用户只能基于一个组 ID 与分组进行关联,于是这种场景就无法满足了。
与同事交流之后,了解了一对一,一对多,多对多的相关概念,上边的大概就属于一对一,而一个分组多个用户就是多对多了。
这种多对多的情境中,一般的做法是引入一个三方表来进行关联,三方表记录用户 ID 与组 ID 的对应关系,从而能够方便地在分组表中查到对应用户。
# 1,一对多。
下边用实际代码简单讲解分析一下一对多的情况,也就是一个用户可以加入不同的组中。
简单示例代码如下:
// User 用户
type User struct {
UserID int `gorm:"primary_key;AUTO_INCREMENT" json:"userId"`
NickName string `gorm:"type:varchar(128)" json:"nickName"` // 昵称
Phone string `gorm:"type:varchar(11)" json:"phone"` // 手机号
Avatar string `gorm:"type:varchar(255)" json:"avatar"` //头像
Sex string `gorm:"type:varchar(255)" json:"sex"` //性别
Email string `gorm:"type:varchar(128)" json:"email"` //邮箱
}
func (User) TableName() string {
return "user"
}
// Group 分组
type Group struct {
GroupID int `gorm:"primary_key;AUTO_INCREMENT" json:"userId"`
GroupName string `gorm:"type:varchar(128)" json:"nickName"` // 组名
}
func (Group) TableName() string {
return "group"
}
// UserGroupRelation 用户与分组映射
type UserGroupRelation struct {
Id uint `gorm:"primary_key;AUTO_INCREMENT";json:"id"`
GroupID uint `gorm:"type:int;unsigned;not null;index:idx_group_id;";json:"group_id"`
UserID uint `gorm:"type:int;unsigned;not null;unique_index:uk_host_id";json:"user_id"`
}
func (UserGroupRelation) TableName() string {
return "user_group_relation"
}
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
这种映射关系下,一般更多时候会有一个展示某个分组中有哪些用户,查询起来应该这么写:
SELECT role.*,user.nick_name FROM `role` left join user on user.role_id=role.role_id WHERE `role`.`deleted_at` IS NULL AND ((`role`.`role_id` = 1));
查询用户处在哪些组内:
SELECT user.*,dept.dept_name FROM user
LEFT JOIN user_dept_relation on user_dept_relation.user_id=user.user_id
LEFT JOIN dept on user_dept_relation.dept_id=dept.dept_id
WHERE (user.user_id=1);
2
3
4