GORM 多对多many2many 自定义连接表

文章目录

  • 多对多 many2many
    • 表结构搭建
    • 多对多添加
    • 多对多查询
    • 多对多的删除、更新
  • 自定义连接表
    • 生成表结构
    • 操作案例
      • 添加文章并添加标签,并自动关联
      • 添加文章,关联已有标签
      • 给已有文章关联标签
      • 替换已有文章的标签
      • 查询文章列表,显示标签
    • 自定义连接表主键
    • 操作连接表
      • 查询

多对多 many2many

Many to Many 会在两个 model 中添加一张连接表,将不同表的id连接起来,也就是说 总共三张表
我们这里以文章和其tag为例,一篇文章可以有多个tag,一个tag中也有多个文章

表结构搭建

type Article struct {
	ID    uint   `gorm:"size:8"`
	Title string `gorm:"size:16"`
	Tags  []Tag  `gorm:"many2many:article_tags;"` //用于确定多对多的关系并指定第三张连接表的名字
}
type Tag struct {
	ID       uint      `gorm:"size:8"`
	Text     string    `gorm:"size:16"`
	Articles []Article `gorm:"many2many:article_tags;"`//反向引用,可以用来查询具有相同标签的文章
}

DB.AutoMigrate(&Article{}, &Tag{})

搭建表结构如图:
在这里插入图片描述

多对多添加

创建标签和文章:
  建立一篇标题为“go”的文章,并新建标签为studylanguage

DB.Save(&Article{
	ID:    1,
	Title: "go",
	Tags: []Tag{
		{Text: "language"},
		{Text: "study"},
	},
})

添加文章,选择标签:这里以选择单个标签为例
如果不查询,即使标签名字一致也会重建一个新的标签

var tag Tag
DB.Take(&tag, "Text=?", "study")
DB.Save(&Article{
	Title: "Study notes",
	Tags:  []Tag{tag},
})

多对多查询

//查询文章,显示文章的标签列表
var article Article
DB.Preload("Tags").Take(&article, 1)
fmt.Println(article)

//查询标签,显示具有该标签的文章列表
var tag Tag
DB.Preload("Articles").Take(&tag, 2)
fmt.Println(tag)

多对多的删除、更新

移除文章的标签

var article Article
DB.Preload("Tags").Take(&article, 1)
DB.Model(&article).Association("Tags").Delete(article.Tags)
fmt.Println(article)

跟新文章的标签

article = Article{}
var tags []Tag

DB.Find(&tags, []int{1, 2, 3}) //找到想要添加的标签

DB.Preload("Tags").Take(&article, 1) //预加载要修改的文章

DB.Model(&article).Association("Tags").Replace(tags) //替换文章标签

自定义连接表

默认的连接表,只有双方的主键id,展示不了更多信息了,为了

type Article struct {
	ID    uint
	Title string
	Tags  []Tag `gorm:"many2many:article2tag;"`
}

type Tag struct {
	ID   uint
	Text string
	//Articles []Article `gorm:"many2many:article2tag;"` //当使用反向引用时需要在setUpJoinTable时多设置一次这个表的 @@@
}

// Article2tag 自定义连接表
type Article2tag struct {
	ArticleID uint      `gorm:"primaryKey"`
	TagID     uint      `gorm:"primaryKey "` //上两项即为连接表默认项
	CreatedAt time.Time //自定义添加一个创建时间字段
}

生成表结构

//第一个参数为具有连接另一个表的字段的连接表,第二个即为连接字段的字段名,第三个为连接表
DB.SetupJoinTable(&Article{}, "Tags", &Article2tag{})
//DB.SetupJoinTable(&Tag{}, "Articles", &Article2tag{}) //与上面反向引用时对应 @@@
DB.AutoMigrate(&Article{}, &Tag{}, &Article2tag{})

操作案例

  我们如果在自定义连接表中设置了默认字段之外的字段的话,需要使用添加钩子beforeCreate来赋值,这里的createAt字段gorm会自动填充,关于钩子函数
  SetupJoinTable:添加和更新是不能注释掉这个,这样才能走自定义的连接表,以及走他的钩子函数,查询则不需要

添加文章并添加标签,并自动关联

DB.Create(&Article{
	Title: "golang_study",
	Tags: []Tag{{Text: "go"},
		{Text: "study"}},
	})

添加文章,关联已有标签

//添加文章,关联已有标签
var tag Tag
DB.Take(&tag, "text=?", "study")
DB.Create(&Article{
	Title: "how To Study",
	Tags:  []Tag{tag},
})

给已有文章关联标签

//直接操作连接表,比较极端,知道文章和tag的ID,直接进行添加-------------
DB.Create(&Article2tag{
	ArticleID: 3,
	TagID:     2,
	CreatedAt: time.Time{},
})

//先查询相关标签,在关联--------------------
var tags []Tag //这里主要根据tag切片作为识别,切片名字可以随便起
DB.Find(&tags, "text in?", []string{"go", "study"})

var article Article
DB.Take(&article, "title=?", "gin")

DB.Model(&article).Association("Tags").Append(&tags)

替换已有文章的标签

//替换已有文章的标签
var article Article
DB.Preload("Tags").Take(&article, 3) //preload中参数需严格对应,这里不需要preload好像也可以:) 
fmt.Println(article)
var tag Tag
DB.Take(&tag, "text=?", "go")
DB.Model(&article).Association("Tags").Replace(&tag)

查询文章列表,显示标签

//查询文章列表并显示标签
var articles []Article
DB.Preload("Tags").Find(&articles)
fmt.Println(articles)

自定义连接表主键

  当定义的表复杂时,gorm自动的主键名可能比较冗长,我们可以通过自定义主键名来简单化
主要要注释的是joinForeignKey 连接主键ID
            joinReferences 关联主键ID

// 自定义主键部分-----------------------------------------
type Article struct {
	ID    uint
	Title string
	Tags  []Tag `gorm:"many2many:article2tag;joinForeignKey:A_ID;joinReferences:T_ID"`
}

type Tag struct {
	ID       uint
	Text     string
	Articles []Article `gorm:"many2many:article2tag;joinForeignKey:T_ID;joinReferences:A_ID"` //当使用反向引用时需要在setUpJoinTable时多设置一次这个表的 @@@
}

// Article2tag 自定义连接表
type Article2tag struct {
	A_ID      uint      `gorm:"primaryKey"`
	T_ID      uint      `gorm:"primaryKey "` //上两项即为连接表默认项
	CreatedAt time.Time //自定义添加一个创建时间字段
}

操作连接表

  当通过其他表来查连接表,可能会不方便,比如我们在这里查二者什么时候建立的关联,即createat字段名

查询

传统查询文章列表,显示标签
首先其无法进行分页操作,经过如下修改,也能分页

DB.Preload("Tags").Take(&article, 1)
var tags []Tag
DB.Model(article).Limit(1).Association("Tags").Find(&tags) //这样可以分页,但是也无法查询连接表中其他字段,如关联时间
fmt.Println(tags)

若是直接在此时的连接表查询,我们也只能得到文章和标签的id,无法得到用户名及标签名
因此我们可以改一下连接表结构,将两张主表的结构体关联到连接表中,具体可见如下代码所示:

// Article2tag 自定义连接表
type Article2tag struct {
	ArticleID uint    `gorm:"primaryKey"`
	TagID     uint    `gorm:"primaryKey "` //上两项即为连接表默认项
	Article   Article `gorm:"foreignKey:ArticleID"`
	Tag       Tag     `gorm:"foreignKey:TagID"`

	CreatedAt time.Time //自定义添加一个创建时间字段
}

查询相关写法可以参见该 链接

var articleTag []Article2tag
DB.Preload("Article").Preload("Tag").Where(map[string]any{"Article_id": 1}).Find(&articleTag) //大小写可忽略,符号不可忽略,具体需查看表内字段
fmt.Println(articleTag)
DB.Preload("Article").Preload("Tag").Where("article_id=?", 1).Find(&articleTag) //大小写可忽略,符号不可忽略,具体需查看表内字段
fmt.Println(articleTag)
DB.Preload("Article").Preload("Tag").Find(&articleTag, "article_id=?", 1) //大小写可忽略,符号不可忽略,具体需查看表内字段
fmt.Println(articleTag)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/218199.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

陀螺仪LSM6DSV16X与AI集成(2)----姿态解算

陀螺仪LSM6DSV16X与AI集成.2--姿态解算 概述视频教学样品申请完整代码下载欧拉角万向节死锁四元数法姿态解算双环PI控制器偏航角陀螺仪解析代码上位机通讯加速度演示陀螺仪工作方式主程序演示 概述 LSM6DSV16X包含三轴陀螺仪与三轴加速度计。 姿态有多种数学表示方式&#xff…

TCL - 库编译过程和官方手册

文章目录 TCL - 库编译过程和官方手册概述笔记编译步骤TCL官方手册END TCL - 库编译过程和官方手册 概述 想看看sqlite3的官方demo工程, 没看到. 想编译一下sqlite3源码, 看看编译后有没有example 工程. 看了sqlite3的官方说明, 他们工程使用tcl来编译的. 一听tcl, 咋这么耳熟…

Ribbon 负载均衡

1、负载均衡整体流程 2、负载均衡流程逐级跟踪运行 (1) LoadBlanced 注解可以使LoadBalancerInterceptor拦截到; (2)LoadBalancerInterceptor 实现了ClientHttpRequestInterceptor接口; (3)ClientHttpRequestInterceptor接口释义如下; (4)int…

k8s引用环境变量

一 定义环境变量 ① 如何在k8s中定义环境变量 env、configmap、secret补充: k8s 创建Service自带的环境变量 ② 从pod属性中获取 kubectl explain deploy.spec.template.spec.containers.env.valueFrom关注: configMapKeyRef、fieldRef 和 resour…

zxjy003- Spring Cloud后端工程搭建

一、创建父工程 1、创建 sprigboot 工程 guli-parent groupId : com.atguigu artifactId : guli-parent

RK3568平台开发系列讲解(Linux系统篇)device_node 转换成 platform_device

🚀返回专栏总目录 文章目录 一、DTB转换规则二、转换源码分析沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍通过设备树 device_node 转换成 platform_device 一、DTB转换规则 device 部分是用 platform_device 结构体来描述硬件资源的, 所以内核最终会将…

【设计模式-3.1】结构型——外观模式

说明:本文介绍设计模式中结构型设计模式中的,外观模式; 亲手下厨还是点外卖? 外观模式属于结构型的设计模式,关注类或对象的组合,所呈现出来的结构。以吃饭为例,在介绍外观模式之前&#xff0…

蓝桥杯网络安全组竞赛

竞赛规则及说明 选拔赛时长:4h 决赛时长:4h 竞赛形式:线上比赛: 个人赛:一人一机,全程机考 大赛制定竞赛系统,在时间内提交答案到比赛系统,超时无法提交 机器环境: 电脑…

matplotlib多子图

matplotlib画图中一个轴占据多个子图 - 知乎 import matplotlib.pyplot as plt fig plt.figure() gs fig.add_gridspec(2,4) ax1 fig.add_subplot(gs[0, 0:2]) ax2 fig.add_subplot(gs[0, 2:]) axa fig.add_subplot(gs[1, 1]) axb fig.add_subplot(gs[1, 2]) axc fig.add…

编写Java应用程序,输出满足1+2+3+……+n<8888的最大正整数n。

源代码: public class Main { public static void main(String[] args) { int i 1; int sum 0; for(i 1;;i){ sum i; if (sum >8888) break; } System.out.println(i-1); } } 实验运行截图:

隐写术和人工智能

在一项新的研究中,人工智能对齐研究实验室 Redwood Research 揭示了大型语言模型 (LLM) 可以掌握“编码推理”,这是一种隐写术形式。 这种有趣的现象使得大型语言模型能够以人类读者无法理解的方式巧妙地将中间推理步骤嵌入到生成的文本中。 大型语言…

【滤波第二期】中值滤波的原理和C代码

中值滤波是一种非线性数字滤波技术,主要应用于信号处理和图像处理领域,用于减小信号中的噪声和离群值。中值滤波的核心思想是通过计算一组数据点的中间值,以抑制脉冲噪声等离群值的影响,从而实现信号的平滑处理。 1,中…

SaToken利用Redis做持久化

官网解释 官网解释 教程 引入依赖 <!-- 提供Redis连接池 --> <dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId> </dependency><!-- Sa-Token 整合 Redis &#xff08;使用 jdk 默认序…

QT使用SQLite(打开db数据库以及对数据库进行增删改查)

QTSQLite 在QT中使用sqlite数据库&#xff0c;有多种使用方法&#xff0c;在这里我只提供几种简单&#xff0c;代码简短的方法&#xff0c;包括一些特殊字符处理。 用SQlite建立一个简单学生管理数据库 数据库中有两个表一个是class和student。 class表结构 student表结果…

GitHub Copilot试用指南

GitHub Copilot试用指南 首先读这个文档&#xff0c;按照步骤开启30天的试用&#xff1a;管理个人帐户的 GitHub Copilot 订阅 然后读这个文档&#xff1a;使用 IDE 中的 GitHub Copilot 聊天 &#xff0c;在你习惯使用的IDE中配置copilot&#xff0c;暂时好像只支持jetbrai…

Android Edittext进阶版(Textfieids)

一、Text fieids 允许用户在 UI 中输入文本&#xff0c;TextInputLayout TextInputEditText。 在 Text fieids 没出来(我不知道)前&#xff0c;想实现这个功能就需要自己自定义控件来实现这个功能。 几年前做个上面这种样式(filled 填充型)。需要多个控件组合 动画才能实现&a…

YoloV5改进策略:Swift Parameter-free Attention,无参注意力机制,超分模型的完美迁移

摘要 https://arxiv.org/pdf/2311.12770.pdf https://github.com/hongyuanyu/SPAN SPAN是一种超分网络模型。SPAN模型通过使用参数自由的注意力机制来提高SISR的性能。这种注意力机制能够增强重要信息并减少冗余,从而在图像超分辨率过程中提高图像质量。 具体来说,SPAN模…

qt 5.15.2 主窗体事件及绘制功能

qt 5.15.2 主窗体事件及绘制功能 显示主窗体效果图如下所示&#xff1a; main.cpp #include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);MainWindow w;w.setFixedWidth(600);w.setFixedHeight(6…

java学习part34collect和map

153-集合框架-数组的特点、弊端与集合框架体系介绍_哔哩哔哩_bilibili 1.以前的数组 2.常用 3.Collection add只能加object&#xff0c;如果有基本类型会装箱 3.2集合和数组转换 3.3往集合添加对象的注意事项 4.迭代器 容易越界 一般不用 常用好用 5.for each 类似c的for( …

鸿蒙4.0开发笔记之ArkTS装饰器语法基础之监听者模式@Watch案例讲解(十四)

1、Watch定义 Watch实际是指状态变量更改通知。如果开发者需要关注某个状态变量的值是否改变&#xff0c;可以使用Watch为状态变量设置回调函数&#xff08;监听函数&#xff09;。 Watch用于监听状态变量的变化&#xff0c;当状态变量变化时&#xff0c;Watch的回调方法将被…
最新文章