• Golang 整合Gorm一对多查询,多对一查询,最新教程,细到极致


       Hello,各位小伙伴,大家好呀。老哥周六要加班,好累…,真的不想加班,但是没有办法,为了Money,只能忍一下。

       老哥趁着下午摸鱼的时间,我们来学习一下Gorm

    1、首先导入gorm包,参考文档,https://learnku.com/docs/gorm/v2/index/9728

    PS D:\Project\Go_Project\goproject1> go get -u gorm.io/gorm
    PS D:\Project\Go_Project\goproject1> go get -u gorm.io/driver/sqlite
    
    • 1
    • 2

    2、然后我们需要使用mysql,导入mysql的驱动包

    D:\Project\Go_Project\goproject1> go get -u github.com/go-sql-driver/mysql
    
    • 1

    3、 gorm连接到Mysql数据库,使用Api,创建students表结构

    补充: 里面有个创建表结构的方法,但是没有数据,老哥这里提供一下数据(当然你也可以自己造 dog狗头保命)

    INSERT INTO students VALUES (1, ‘Jessica’, 25);
    INSERT INTO students VALUES (2, ‘Jerry’, 26);
    INSERT INTO students VALUES (3, ‘Tom’, 18);
    INSERT INTO students VALUES (4, ‘lili’, 18);

    package main
    
    import (
    	"fmt"
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    	"log"
    )
    
    type Student struct {
    	Id   int
    	Name string
    	Age  int
    }
    
    func initGormDb() *gorm.DB {
    	dsn := "root:root@tcp(127.0.0.1:3306)/go-test?charset=utf8mb4&parseTime=True&loc=Local"
    	dbs, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
    		//如果不想看到日志sql,这里可以关掉
    		Logger: logger.Default.LogMode(logger.Info),
    	})
    	if err != nil {
    		panic("gorm failed to connect Mysql...")
    	}
    	return dbs
    }
    
    // CreateTable 创建表
    func CreateTable(db *gorm.DB) {
    	err := db.AutoMigrate(&Student{})
    	if err != nil {
    		log.Println("failed to create table...")
    		return
    	}
    }
    
    func main() {
    	db := initGormDb()
    
    	//1、创建表
    	CreateTable(db)
    
    }
    
    • 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

    3A、插入数据,支持批量插入(CreateInBatches ),具体参考,https://learnku.com/docs/gorm/v2/create/9732

    //InsertData
    func InsertData(db *gorm.DB) {
    	//插入一条
    	//p := Student{
    	//	Name: "uzi",
    	//	Age:  25,
    	//}
    	//tx := db.Create(&p)
    	//fmt.Println(tx.RowsAffected)
    
    	//插入多条
    	p2 := Student{
    		Name: "gala",
    		Age:  20,
    	}
    
    	p3 := Student{
    		Name: "wei",
    		Age:  19,
    	}
    
    	txs := db.Create(&[]Student{p2, p3})
    	fmt.Println(txs.RowsAffected)
    }
    
    
    • 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

    5、查询数据,因为查询的话,没有固定的写法,老哥这里只是列举了常用的,详情参考,https://learnku.com/docs/gorm/v2/advanced_query/9757

    // SelectData 查询数据
    /**
    查询五花八门,我们可以按照需要进行定制查询
    */
    func SelectData(db *gorm.DB) {
    	//1、保存多条数据
    	var stuList []Student
    
    	//2、保存单条数据
    	var stu Student
    
    	//按照主键查找
    	//db.First(&stu, 2)
    
    	//带条件查询,查一条
    	//db.First(&stu, "age=?", 18)
    
    	// 获取最后一条记录(主键降序)
    	//db.Last(&stu)
    
    	//获取全部数据
    	//db.Find(&stuList)
    
    	//where查询单条数据
    	//db.Where("age=?", 25).Find(&stu)
    	//db.Where(&Student{Age: 18}).Find(&stu)
    	//fmt.Println(stu)
    
    	//where查询多条数据
    	//db.Where("age=?", 18).Find(&stuList)
    
    	//where查询多条数据 or
    	//db.Where("age=? or age=?", 25, 26).Find(&stuList)
    	//db.Where("age=?", 18).Or("age=?", 26).Find(&stuList)
    	//fmt.Println(stuList)
    
    	//where查询通过单条数据 Map
    	db.Where(map[string]interface{}{"name": "uzi", "age": 25}).Find(&stu)
    	fmt.Println(stu)
    	
        //BETWEEN 查询年龄25到30
    	//db.Where("age BETWEEN ? AND ?", 25, 30).Find(&stuList)
    	//fmt.Println(stuList)
    
    	//order查询 按照年龄降序,默认是升序
    	//db.Order("age desc").Find(&stuList)
    	//fmt.Println(stuList)
    
    	//limit查询,查询前3条数据
    	//db.Limit(3).Find(&stuList)
    	//fmt.Println(stuList)
    
    	//Offset 跳过几条,查询前3条数据
    	//db.Limit(3).Offset(1).Find(&stuList)
    	//fmt.Println(stuList)
    
    	//Distinct 按照年龄去重,返回不重复的年龄
    	//db.Distinct("age").Find(&stuList)
    	//fmt.Println(stuList)
    
    	//Group分组 等同于 select age from students group by age
    	//db.Select("age").Group("age").Find(&stuList)
    	//fmt.Println(stuList)
    
    	//Having 查询年龄大于20
    	db.Having("age > ?", 20).Find(&stuList)
    	fmt.Println(stuList)
    }
    
    
    • 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

    6、更新数据,参考资料,https://learnku.com/docs/gorm/v2/update/9734

    // UpdateStudentData 更新数据
    func UpdateStudentData(db *gorm.DB) {
    	//单条更新 修改Jessica的年龄为18
    	//update := db.Model(Student{}).Where("name=?", "Jessica").Update("age", 18)
    	//fmt.Println(update.RowsAffected)
    
    	//批量更新 修改id为 6、7,年龄统一改为19
    	updates := db.Table("students").Where("id In ?", []int{6, 7}).Updates(map[string]interface{}{"age": 19})
    	fmt.Println(updates.RowsAffected)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    7、删除数据,注意这里增加了一个钩子,在删除之前,我们可以加一些属于自己的逻辑

    //DeleteData 删除数据
    func DeleteData(db *gorm.DB) {
    	//单条删除 删除id为7的数据
    	//deleteCount := db.Delete(&Student{}, "7")
    	//fmt.Println(deleteCount.RowsAffected)
    
    	//批量删除 删除id为5、6的数据
    	//deleteCountList := db.Delete(&Student{}, []int{5, 6})
    	//fmt.Println(deleteCountList.RowsAffected)
    
    	//删除钩子,因为删除一般是危险动作,我们可以考虑删除加个判断
    	//BeforeDelete 会在删除之前,自动调用
    
    	//下面的方法均不会触发钩子
    	//1.
    	//db.Delete(&User{}, 1)
    	2.
    	//db.Delete(&User{}, "id in ?", ids)
    	3.
    	//var ids = []uint{1,2,3}
    	//db.Where("id in ?",ids).Delete(&User{})
    
        //钩子触发条件,stu里面必须有数据 
    	//下面这种会触发钩子,单条删除
    	//var stu Student
    	//db.Where("id =3").Find(&stu).Delete(&stu)
    
    	//下面这种会触发钩子,多条删除
    	//var stuList []Student
    	//var ids = []uint{1, 2, 3}
    	//db.Where("id in ?", ids).Find(&stuList).Delete(&stuList)
    
    	//删除全部数据,不会触发钩子
    	db.Where("id > ?", 0).Delete(&Student{})
    }
    
    func (stu *Student) BeforeDelete(tx *gorm.DB) (err error) {
    	if stu.Name == "Jerry" {
    		fmt.Println("Jerry not allowed to delete")
    		return errors.New("Jerry not allowed to delete")
    	}
    	return
    }
    
    • 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

    8、gorm基本的增删改查先介绍到这里,已经可以满足开发的日常需要。下面介绍一下,Gorm进阶
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->

    9、gorm 支持我们写原生Sql,最大限度让我们自由发挥

    //RawSql 支持原生sql
    func RawSql(db *gorm.DB) {
    	var stuList []Student
    	// SELECT * FROM students WHERE name = "Jessica" OR name2 = "Tom"
    	db.Raw("SELECT * FROM students WHERE name = @name1 OR name = @name2",
    		sql.Named("name1", "Jessica"), sql.Named("name2", "Tom")).Find(&stuList)
    	fmt.Println(stuList)
    
    	// UPDATE students SET age = 20 where id in (2,3)"
    	//db.Exec("UPDATE students SET age = @ages where id in (2,3)",
    	//	sql.Named("ages", 20))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    10、一对多, 我们先创建表结构,然后插入数据

    type Company struct {
    	ID      uint `gorms:"primarykey"`
    	Name    string
    	Workers []Worker //用来存放查询出来的所有打工人
    }
    
    type Worker struct {
    	ID         uint `gorms:"primarykey"`
    	WorkerName string
    	CompanyID  uint
    }
    
    func main() {
    	db := initGormDb()
    
    	//创建表,先有公司,然后有打工人
    	//db.AutoMigrate(&Company{}, &Worker{})
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    INSERT INTO companies VALUES (1, ‘jdg’);

    INSERT INTO workers VALUES (‘yagao’, 1);
    INSERT INTO workers VALUES (‘369’, 1);
    INSERT INTO workers VALUES (‘kanavi’, 1);
    INSERT INTO workers VALUES (‘hope’, 1);
    INSERT INTO workers VALUES (‘missing’, 1);

    package main
    
    import (
    	"bytes"
    	"encoding/json"
    	"fmt"
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    	"gorm.io/gorm/logger"
    )
    
    func initGormDb() *gorm.DB {
    	dsn := "root:root@tcp(127.0.0.1:3306)/go-test?charset=utf8mb4&parseTime=True&loc=Local"
    
    	dbs, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
    		//如果不想看到日志sql,这里可以关掉
    		Logger: logger.Default.LogMode(logger.Info),
    	})
    
    	if err != nil {
    		panic("gorms failed to connect Mysql...")
    	}
    	return dbs
    }
    
    type Company struct {
    	ID      uint `gorms:"primarykey"`
    	Name    string
    	Workers []Worker //用来存放查询出来的所有打工人
    }
    
    type Worker struct {
    	ID         uint `gorms:"primarykey"`
    	WorkerName string
    	CompanyID  uint
    }
    
    func PrintJsonTree(obj interface{}) {
    	marshal, err := json.Marshal(obj)
    
    	//这里是把JSON格式,树形化输出,优雅地展示
    	var treeJSON bytes.Buffer
    	err = json.Indent(&treeJSON, marshal, "", "\t")
    
    	fmt.Println(string(treeJSON.Bytes()))
    
    	if err != nil {
    		panic(err)
    	}
    }
    
    func main() {
    	db := initGormDb()
    
    	//创建表,先有公司,然后有打工人
    	//db.AutoMigrate(&Company{}, &Worker{})
    
    	//1、一对多,一个公司有多名员工,插入数据
    	//w1 := Worker{
    	//	ID:         8,
    	//	WorkerName: "knight",
    	//}
    	//
    	//w2 := Worker{
    	//	ID:         9,
    	//	WorkerName: "tian",
    	//}
    	//
    	//c := Company{
    	//	ID:      3,
    	//	Name:    "tes",
    	//	Workers: []Worker{w1, w2},
    	//}
    	//
    	//db.Create(&c)
    
    	//2、一对多,一个公司有多名员工,查询数据
    	//查询公司为jdg的信息
    	var company Company
    	
    	//注意:Workers取得是 Workers []Worker 的变量,Preload预加载
    	//它会先 SELECT * FROM `workers` WHERE `workers`.`company_id` = 1
    	//然后  SELECT * FROM `companies` WHERE `companies`.`id` = '1'
    	db.Preload("Workers").Find(&company, "1")
    	
    	//Preload 还可以增加你想要的条件,比如,我只带369出来
    	//Worker_Name 对应数据库的 Worker_Name
    	//db.Preload("Workers", "Worker_Name=?", "369").Find(&company, "1")
    
    	PrintJsonTree(company)
    
    }
    
    • 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

    一对多,打印结果如下:

    在这里插入图片描述

    11、多对一

    	//多对一查询
    	//多个打工人,对应一个公司,查询数据
    	var WorkList []Worker
    	//查询所有在jdg的员工信息,多对一
    	db.Where("company_id =? ", 1).Find(&WorkList)
    
    	PrintJsonTree(WorkList)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    多对一,查询结果如下:

    在这里插入图片描述

    12、多对多,注意:多对多插入数据要分两次插入,因为中间表的插入,第二次不能再新增ID

    package main
    
    import (
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    	"gorm.io/gorm/logger"
    )
    
    func initGormDb() *gorm.DB {
    	dsn := "root:root@tcp(127.0.0.1:3306)/go-test?charset=utf8mb4&parseTime=True&loc=Local"
    
    	dbs, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
    		//如果不想看到日志sql,这里可以关掉
    		Logger: logger.Default.LogMode(logger.Info),
    	})
    
    	if err != nil {
    		panic("gorms failed to connect Mysql...")
    	}
    	return dbs
    }
    
    // User 拥有并属于多种 language,`user_languages` 是连接表
    type User struct {
    	gorm.Model
    	UserName  string
    	Languages []Language `gorm:"many2many:user_languages;"`  //加了这个,需要绑定另外一个many2many,然后会创建一张中间表
    }
    
    type Language struct {
    	gorm.Model
    	Name  string
    	Users []User `gorm:"many2many:user_languages;"` //加了这个,需要绑定另外一个many2many,然后会创建一张中间表
    }
    
    //多对多
    func main() {
    	db := initGormDb()
    
    	db.AutoMigrate(&User{}, &Language{})  //第一次插入需要
    
    	u1 := User{
    		UserName: "大佬1号",
    	}
    
    	u2 := User{
    		UserName: "大佬2号",
    	}
    
    	l1 := Language{
    		Name:  "English",
    		Users: []User{u1, u2},
    	}
    	//
    	第一次插入数据,英语有两个大佬学
    	db.Create(&l1)
    
    }
    
    • 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

    第二次插入数据,u1 u2 l2三处调整

    //多对多
    func main() {
    	db := initGormDb()
    
    	//db.AutoMigrate(&User{}, &Language{})
    
    	u1 := User{
    		Model:gorm.Model{
    			ID: 1,
    		},
    		UserName: "大佬1号",
    	}
    
    	u2 := User{
    		Model:gorm.Model{
    			ID: 2,
    		},
    		UserName: "大佬2号",
    	}
    
    	//l1 := Language{
    	//	Name:  "English",
    	//	Users: []User{u1, u2},
    	//}
    
    	l2 := Language{
    		Name:  "Math",
    		Users: []User{u1, u2},
    	}
    	
    	
    	//
    	第一次插入数据,英语有两个大佬学
    	//db.Create(&l1)
    	
    	//第二次插入,数学有两个大佬学
    	db.Create(&l2)
    
    }
    
    • 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

    如果是正常插入,中间表 user_languages 的显示如下:

    在这里插入图片描述

    13、然后多对多,打印输出

    func PrintJsonTrees(obj interface{}) {
    	marshal, err := json.Marshal(obj)
    
    	//这里是把JSON格式,树形化输出,优雅地展示
    	var treeJSON bytes.Buffer
    	err = json.Indent(&treeJSON, marshal, "", "\t")
    
    	fmt.Println(string(treeJSON.Bytes()))
    
    	if err != nil {
    		panic(err)
    	}
    }
    
    
    //多对多
    func main() {
    	db := initGormDb()
    	
    	//多对多查询
    	var UserList []User
    	
    	db.Preload("Languages").Find(&UserList)
    
    	PrintJsonTrees(UserList)
    
    }
    
    
    • 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

    多对多,结果查询如下:

    在这里插入图片描述

    14、最后,各位小伙伴,麻烦给老哥一个点赞、关注、收藏三连好吗,你的支持是老哥更新最大的动力,谢谢!

  • 相关阅读:
    C++之类与对象(2)
    超级详细的mysql安装和配置教程
    sqlserver创建新用户并指定数据库
    PAT乙级 1011 A+B 和 C
    上四休三,未来的期许
    手机扫描二维码的测试用例
    【C++进阶】:AVL树(平衡因子)
    SpringMvc决战-【SpringMVC之自定义注解】
    软件设计师第4题
    debian11 使用apt安装php, 使php多版本共存
  • 原文地址:https://blog.csdn.net/gubeichengxuyuan/article/details/126656975