• gorm Preload与Joins带条件查询--闭坑指南与示例


    一. Preload与Joins的区别

    • Preload方法是用来加载关联字段(belongTomany2manyhasOnehasMany)的数据的。
    • gorm中的Joins方法仅适用的查询,无法加载关联字段内容。并且,gorm原生的方法只支持一对一关系(has one, belongs to)。

    二. 使用示例

    示例中用到的结构体如下

    type Child struct {
    	gorm.Model
    	ChildName string `gorm:"column:child_name;unique"`
    	Toys      []Toy  `gorm:"foreignKey:ChildId"`
    }
    func (Child) TableName() string {
    	return "child"
    }
    
    type Toy struct {
    	gorm.Model
    	Name    string `gorm:"column:name"`
    	ChildId uint   `gorm:"column:child_id"`
    	Child   *Child `gorm:"foreignKey:ChildId"`
    }
    func (Toy) TableName() string {
    	return "toy"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    并生成以下数据

    var children = []Child{
    	{
    		ChildName: "刘涛",
    		Toys: []Toy{
    			{Name: "纸飞机"},
    			{Name: "小火车"},
    		},
    	},
    	{
    		ChildName: "王斌",
    		Toys: []Toy{
    			{Name: "玩具兵"},
    		},
    	},
    }
    db.Create(&children)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    1. Preload

    1.1 Preload 带条件参数查询

    例子1:有个活动需要带所有孩子都参加,但是每个孩子都只能带名为纸飞机的玩具。

    使用如下查询方法:

    var children []Child
    db.Preload("Toys", db.Where(&Toy{Name: "纸飞机"})).Find(&children)
    //也可以写成
    //db.Preload("Toys", "name = ?", "纸飞机").Find(&children)
    
    • 1
    • 2
    • 3
    • 4

    等同于sql语句

    SELECT * FROM "toy" WHERE "toy"."child_id" IN (1,2) AND name =  '纸飞机' AND "toy"."deleted_at" IS NULL;
    SELECT * FROM "child" WHERE "child"."deleted_at" IS NULL;
    
    • 1
    • 2

    输出结果转为json(为了易于查看,省略了一些不必要的字段):

    [
      {
        "ID": 1,
        "ChildName": "刘涛",
        "Toys": [
          {
            "ID": 1,
            "ChildId": 1,
            "Name": "纸飞机"
          }
        ]
      },
      {
        "ID": 2,
        "ChildName": "王斌",
        "Toys": []
      }
    ]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    可以看到,王斌小朋友也出现在了查询结果中,但是他的"Toys”是空的。因为你的查询条件只允许小朋友们带纸飞机。

    例子2:但是每个人只展示一个玩具

    实际开发中,通常用于预览页。示例代码如下:

    var children []Child
    limit1:=func(db *gorm.DB) *gorm.DB {return db.Limit(1)}
    db.Preload("Toys",limit1).Find(&children)
    
    • 1
    • 2
    • 3

    2 Joins

    2.1 Joins 带条件查询

    因为joins只支持has onebelongs to,所有这里的例子换个方向:查询哪些玩具是属于刘涛的。
    查询示例如下:

    var toys []Toy
    //注意where中的表别名一定要加双引号
    db.Joins("Child").Where("\"Child\".child_name = ?", "刘涛").Find(&toys)
    
    //也可以写成这样,这里是不需要双引号的
    db.Debug().Joins("Child").Clauses(clause.Eq{
    	Column: "Child.child_name",
    	Value:  "刘涛",
    }).Find(&toys)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    等同于sql:

    SELECT "toy"."id","toy"."created_at","toy"."updated_at","toy"."deleted_at","toy"."child_id","toy"."name","Child"."id" AS "Child__id","Child"."created_at" AS "Child__created_at","Child"."updated_at" AS "Child__updated_at","Child"."deleted_at" AS "Child__deleted_at","Child"."child_name" AS "Child__child_name" FROM "toy" LEFT JOIN "child" "Child" ON "toy"."child_id" = "Child"."id" AND "Child"."deleted_at" IS NULL WHERE "Child".child_name = '刘涛' AND "toy"."deleted_at" IS NULL
    
    • 1

    主要是这断话:LEFT JOIN "child" "Child",gorm自动給表起了一个别名Child

    查询结果:

    [
      {
        "ID": 1,
        "CreatedAt": "2022-11-09T19:51:38.008533+08:00",
        "UpdatedAt": "2022-11-09T19:51:38.008533+08:00",
        "DeletedAt": null,
        "ChildId": 1,
        "Child": {
          "ID": 1,
          "CreatedAt": "2022-11-09T19:51:38.006354+08:00",
          "UpdatedAt": "2022-11-09T19:51:38.006354+08:00",
          "DeletedAt": null,
          "ChildName": "刘涛",
          "Toys": null
        },
        "Name": "纸飞机"
      },
      {
        "ID": 2,
        "CreatedAt": "2022-11-09T19:51:38.008533+08:00",
        "UpdatedAt": "2022-11-09T19:51:38.008533+08:00",
        "DeletedAt": null,
        "ChildId": 1,
        "Child": {
          "ID": 1,
          "CreatedAt": "2022-11-09T19:51:38.006354+08:00",
          "UpdatedAt": "2022-11-09T19:51:38.006354+08:00",
          "DeletedAt": null,
          "ChildName": "刘涛",
          "Toys": null
        },
        "Name": "小火车"
      }
    ]
    
    • 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

    2.2 Joins 模糊查询

    模糊查询最省力的方法是使用子句构造器,其它类似的方法就不展示了:

    //查询拥有‘汽车’玩具的孩子们
    db = db.Joins("Toys").Clauses(clause.Like{
    			Column: "Toys.name",
    			Value:  "%" + "汽车" + "%",
    		}).Find(&children)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.3 使Joins支持一对多关系的查询

    这个方法其实也支持多对多关系,和sql语法是原理是一样的。
    例子:查询有纸飞机玩具的孩子们,没有的孩子不展示。

    var children []Child
    db.Table(fmt.Sprintf("%v AS t1", Child{}.TableName())).
    	Joins(fmt.Sprintf("LEFT JOIN %v AS t2 on t1.id =t2.child_id ", Toy{}.TableName())).
    	Where("t2.name = ?", "纸飞机").Find(&children)
    
    • 1
    • 2
    • 3
    • 4

    等同于sql语句

    SELECT "t1"."id","t1"."created_at","t1"."updated_at","t1"."deleted_at","t1"."child_name" FROM child AS t1 left join toy t2 on t1.id =t2.child_id  WHERE t2.name = '纸飞机' AND "t1"."deleted_at" IS NULL
    
    • 1

    查询结果

    [
      {
        "ID": 1,
        "CreatedAt": "2022-11-09T19:51:38.006354+08:00",
        "UpdatedAt": "2022-11-09T19:51:38.006354+08:00",
        "DeletedAt": null,
        "ChildName": "刘涛",
        "Toys": null
      }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以看到,查询结果没问题,但是Toys是空的。加一个Preload方法就行了

    db.Preload("Toys").
       Table(fmt.Sprintf("%v AS t1", Child{}.TableName())).
    		Joins(fmt.Sprintf("LEFT JOIN %v AS t2 on t1.id =t2.child_id ", Toy{}.TableName())).
    		Where("t2.name = ?", "纸飞机").Find(&children)
    
    • 1
    • 2
    • 3
    • 4

    输出结果

    [
      {
        "ID": 1,
        "CreatedAt": "2022-11-09T19:51:38.006354+08:00",
        "UpdatedAt": "2022-11-09T19:51:38.006354+08:00",
        "DeletedAt": null,
        "ChildName": "刘涛",
        "Toys": [
          {
            "ID": 1,
            "CreatedAt": "2022-11-09T19:51:38.008533+08:00",
            "UpdatedAt": "2022-11-09T19:51:38.008533+08:00",
            "DeletedAt": null,
            "ChildId": 1,
            "Child": null,
            "Name": "纸飞机"
          },
          {
            "ID": 2,
            "CreatedAt": "2022-11-09T19:51:38.008533+08:00",
            "UpdatedAt": "2022-11-09T19:51:38.008533+08:00",
            "DeletedAt": null,
            "ChildId": 1,
            "Child": null,
            "Name": "小火车"
          }
        ]
      }
    ]
    
    • 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

    2.4 Joins方法的错误示范

    需要注意的是,Joins方法的查询条件需要放在Where方法中。
    但是很多人会和Preload方法混淆,写成这样

    //错误示例1
    db.Joins("Child", db.Where(&Child{ChildName: "刘涛"})).Find(&toys)
    //错误示例2
    db.Joins("Child", "Child.child_name = '刘涛'").Find(&toys)
    
    • 1
    • 2
    • 3
    • 4
    • 错误示例1 查询出来的玩具兵也变成刘涛的了,完全不对;
      官方文档 中虽然有介绍到这个方法,但是实际使用中意义不明,而且还存在bug。
      在这里插入图片描述

    • 错误示例2 完全无意义的查询方法。

      [
        {
          "ID": 1,
          "CreatedAt": "2022-11-09T19:51:38.008533+08:00",
          "UpdatedAt": "2022-11-09T19:51:38.008533+08:00",
          "DeletedAt": null,
          "ChildId": 1,
          "Child": {
            "ID": 1,
            "CreatedAt": "2022-11-09T19:51:38.006354+08:00",
            "UpdatedAt": "2022-11-09T19:51:38.006354+08:00",
            "DeletedAt": null,
            "ChildName": "刘涛",
            "Toys": null
          },
          "Name": "纸飞机"
        },
        {
          "ID": 2,
          "CreatedAt": "2022-11-09T19:51:38.008533+08:00",
          "UpdatedAt": "2022-11-09T19:51:38.008533+08:00",
          "DeletedAt": null,
          "ChildId": 1,
          "Child": {
            "ID": 1,
            "CreatedAt": "2022-11-09T19:51:38.006354+08:00",
            "UpdatedAt": "2022-11-09T19:51:38.006354+08:00",
            "DeletedAt": null,
            "ChildName": "刘涛",
            "Toys": null
          },
          "Name": "小火车"
        },
        {
          "ID": 3,
          "CreatedAt": "2022-11-09T19:51:38.008533+08:00",
          "UpdatedAt": "2022-11-09T19:51:38.008533+08:00",
          "DeletedAt": null,
          "ChildId": 2,
          "Child": {
            "ID": 2,
            "CreatedAt": "2022-11-09T19:51:38.006354+08:00",
            "UpdatedAt": "2022-11-09T19:51:38.006354+08:00",
            "DeletedAt": null,
            "ChildName": "王斌",
            "Toys": null
          },
          "Name": "玩具兵"
        }
      ]
      
      • 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
  • 相关阅读:
    生活旅游数据恢复:全国违章查询
    【智能算法】回溯搜索算法(BSA)原理及实现
    从零开始学习opencv——在虚拟环境下安装opencv环境
    uniapp插件开发
    ctrl+d和ctrl+c的区别
    如何熟练掌握分子动力学LAMMPS软件模拟
    torch.cuda.OutOfMemoryError: CUDA out of memory.
    java拦截器
    支持向量机基本原理,Libsvm工具箱详细介绍,基于支持向量机SVM的遥感图像分类
    Java实现常用的排序算法(快速排序、归并排序、基数排序)
  • 原文地址:https://blog.csdn.net/dorlolo/article/details/127774381