二丫讲梵 二丫讲梵
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)

二丫讲梵

行者常至,为者常成
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)
  • Shell编程

  • Go编程笔记

    • 开发技巧

    • 库包研究

      • 使用gorm进行联合查询的整理总结
        • 1,前言
        • 2,实践
        • 3,Tips
          • 1,模型规划
          • 2,表名汇总
          • 3,包容关联
        • 再思考
          • 1,一对多。
      • 一个ftp客户端的封装
      • 使用go-bindata将文件编译进二进制
      • go-gitlab包源码探寻与心得
      • 利用cobra库快速开发类似kubectl一样的命令行工具
      • 使用MongoDB官方go库操作MongoDB
      • 再探-利用gorm自身提供的方法实现MySQL中数据关联的能力
      • 使用retry-go给项目添加重试机制
      • go-cache包的使用简析
      • 利用gorm自身提供的方法实现存在更新不存在则创建的能力
      • 近期关于cobra库的一些实践心得总结
    • 个人项目

  • 前端编程笔记

  • Go学习笔记

  • Vue-21年学习笔记

  • Vue-22年重学笔记

  • 编程世界
  • Go编程笔记
  • 库包研究
二丫讲梵
2020-06-17
目录

使用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
}
1
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)
}
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
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

代码简述:

如上代码首先声明了三个模型,然后通过三个模型创建了三张表,接着造了一些数据,然后从不同的维度对三张表进行关联查询。

虽然是个简单的验证学习程序,但是利用空闲时间全部自己手敲,不断踩坑摸索,也足足搞了两天,希望看到此处的你能在这段代码中获得些许启迪与帮助。

文章还没完,夏夜深深,先上一张美女图片清心,然后再进入技巧分享。

img

# 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
}
1
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"
}
1
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"`
}
1
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"
}
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

这种映射关系下,一般更多时候会有一个展示某个分组中有哪些用户,查询起来应该这么写:

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));
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);
1
2
3
4
微信 支付宝
#golang#gorm#mysql
上次更新: 2024/07/04, 22:40:37
Go开发实践之Gin框架将前端的dist目录embed到二进制
一个ftp客户端的封装

← Go开发实践之Gin框架将前端的dist目录embed到二进制 一个ftp客户端的封装→

最近更新
01
学习周刊-总第214期-2025年第23周
06-05
02
学习周刊-总第213期-2025年第22周
05-29
03
学习周刊-总第212期-2025年第21周
05-22
更多文章>
Theme by Vdoing | Copyright © 2017-2025 | 点击查看十年之约 | 浙ICP备18057030号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式