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

  • Post author:
  • Post category:golang


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

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

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

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)

}

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)
}

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)
}

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)
}

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
}

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))
}

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{})
}

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)

}

一对多,打印结果如下:

在这里插入图片描述

11、多对一

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

	PrintJsonTree(WorkList)

多对一,查询结果如下:

在这里插入图片描述

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)

}

第二次插入数据,

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)

}

如果是正常插入,中间表 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)

}

多对多,结果查询如下:

在这里插入图片描述

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



版权声明:本文为gubeichengxuyuan原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。