gorm 多对多关系
多对多关系,需要用第三张表存储两张表的关系
连接数据库
main.go
package main
import "gorm-demo/model"
func main() {
model.InitMysql()
}
model/dbInit.go
package model
import (
"fmt"
"log"
"os"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
)
var Db *gorm.DB
const (
Username = "root"
Password = "123456"
Host = "127.0.0.1"
Port = "3306"
DbName = "gorm_demo" // 库名
Config = "charset=utf8mb4&parseTime=True&loc=Local"
TablePrefix = "" // 表前缀t_
// 数据库连接池最大空闲数
MaxIdleConns = 10
// 数据库连接池最大打开数
MaxOpenConns = 100
// 数据库连接池最大存活时间
ConnMaxLifetime = time.Hour * 1
)
// 初始化数据库并产生数据库全局变量
func InitMysql() {
// 默认的级别,会打印find找不到模型时的sql语句。
// Silent 就不会。
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // Log level 可选 Silent,Error,Warn,Info
Colorful: true, // 禁用彩色打印 true:不禁用 false:禁用
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
},
)
//"root:123456@tcp(127.0.0.1:3306)/gorm_demo?charset=utf8mb4&parseTime=True&loc=Local",
dns := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?%s", Username, Password, Host, Port, DbName, Config)
db, err := gorm.Open(mysql.New(mysql.Config{
//注意:想要正确的处理 time.Time ,您需要带上 parseTime 参数, (更多参数) 要支持完整的 UTF-8 编码,您需要将 charset=utf8 更改为 charset=utf8mb4 查看 此文章 获取详情
DSN: dns, // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度 utf8mb4长应该设置为171,否则如果以字符串作为主键,会报索引超长
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置 //根据版本自动配置 // 根据当前 MySQL 版本自动配置
}), &gorm.Config{
//CreateBatchSize: 1000, // 批量插入的条数
SkipDefaultTransaction: false, //为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,您可以在初始化时禁用它。 true:禁用 / false 不禁用
DisableForeignKeyConstraintWhenMigrating: true, // 外键约束 在 AutoMigrate 或 CreateTable 时,GORM 会自动创建外键约束,若要禁用该特性,可将其设置为 true
Logger: newLogger,
NamingStrategy: schema.NamingStrategy{
TablePrefix: TablePrefix, // 表名前缀,`User` 的表名应该是 `t_users`
SingularTable: true, // 使用单数表名,启用该选项,此时,`User` 的表名应该是 `t_user`
NoLowerCase: false, // 禁用自动将字段名转换为小写
},
})
if err != nil {
fmt.Println("gorm.Open err:", err)
panic("gorm.Open err")
} else {
Db = db
sqlDB, _ := Db.DB()
// Enable Logger, show detailed log
sqlDB.SetMaxIdleConns(MaxIdleConns) //设置空闲连接池中连接的最大数量
sqlDB.SetMaxOpenConns(MaxOpenConns) //设置打开数据库连接的最大数量。
sqlDB.SetConnMaxLifetime(ConnMaxLifetime) //设置了连接可复用的最大时间。
fmt.Println("")
fmt.Println("InitMysql success")
fmt.Println("")
autoMigrate() //迁移文件
}
}
// 迁移文件
func autoMigrate() {
//belongstoDemo01()
//belongstoDemo02()
//belongstoDemo03()
//belongstoDemo04()
//hasOneDemo01()
//hasOneDemo02()
//hasOneDemo03()
//hasOneDemo04()
//hasOneDemo05()
//hasmanyDemo01()
//hasmanyDemo02()
manyToManyDome01()
//manyToManyDome02()
//manyToManyDome03()
// 实体关联
//association_demo01()
//testDemo01()
}
model/manyToMany.go
package model
import (
"fmt"
"gorm.io/gorm"
)
type Info struct {
gorm.Model
Money int
DogID uint
}
type Dog struct {
gorm.Model
Name string
Info Info
GirlGods []GirlGod `gorm:"many2many:dog_girl_god"` //定义中间表dog_girl_god
}
type GirlGod struct {
gorm.Model
Name string
Dogs []Dog `gorm:"many2many:dog_girl_god"` //定义中间表dog_girl_god
}
func manyToManyDome01() {
Db.AutoMigrate(&Info{}, &Dog{}, &GirlGod{})
//create01()
//create02()
//getGodsDome01()
//查找关联
//getGodsDome02()
//修改关联关系 update Replace Delete
updateDome01()
//Delete with Select
//删除记录的同时,也删除他所关联的数据
//selectDelete()
}
// 创建狗狗1号,创建女神1,2号,并做关系
// 狗狗1号,女神1号和女神2号都会被创建,并在关联表里创建关联关系
func create01() {
Info01 := Info{Money: 100}
girlGod01 := GirlGod{Name: "GirlGod01"}
girlGod02 := GirlGod{Name: "GirlGod02"}
//write
d := Dog{
Name: "dog01",
GirlGods: []GirlGod{girlGod01, girlGod02},
Info: Info01,
}
Db.Create(&d)
}
// 创建狗狗2号,并关联女神1号和女神2号,
func create02() {
// 方式一,前端传入女神1号和女神2号的id
// 如果女神1号和女神2号已经存在,就不在创建了.
//girlGod1 := GirlGod{
// Model: gorm.Model{ID: 1},
// Name: "女神1号",
//}
//girlGod2 := GirlGod{
// Model: gorm.Model{ID: 2},
// Name: "女神2号",
//}
//d02 := Dog{
// Name: "dog02",
// Info: Info{Money: 20000},
// GirlGods: []GirlGod{girlGod1, girlGod2},
//}
//Db.Create(&d02)
//方式二 从表里查出要关联的女神
var girlGods []GirlGod
Db.Find(&girlGods, "id in (?)", []int{1, 2})
d02 := Dog{
Name: "dog02",
Info: Info{Money: 20000}, //创建info表信息
GirlGods: girlGods,
}
Db.Create(&d02)
}
func getGodsDome01() {
var dog Dog
Db.Find(&dog)
fmt.Println(dog)
}
func getGodsDome02() {
//查dogs表 预加载GirlGods
//var dogs []Dog
//Db.Preload("GirlGods").Find(&dogs)
//fmt.Println(dogs)
//查dogs表 预加载GirlGods和dogs的Info
//var dogs []Dog
//Db.Preload("Info").Preload("GirlGods").Find(&dogs)
//fmt.Println(dogs)
//查GirlGods 预加载Dogs和Dogs.Info
//var girl []GirlGod
//Db.Preload("Dogs.Info").Preload("Dogs").Find(&girl)
//fmt.Println(girl)
//查Dog表id为1的GirlGods信息(要的结果是girlGod信息,和GirlGods下的Dogs和Dogs.Info)
//`dog` 是源模型,它的主键不能为空
var girlGod []GirlGod
//查Dog表,关联GirlGods表(Association是做关联查询)
//Db.Model(&Dog{Model:gorm.Model{ID: 1}}).Association("GirlGods").Find(&girlGod)
//Db.Model(&Dog{Model:gorm.Model{ID: 1}}).Preload("Dogs").Association("GirlGods").Find(&girlGod)
//Db.Model(&Dog{Model:gorm.Model{ID: 1}}).Preload("Dogs.Info").Association("GirlGods").Find(&girlGod)
//Db.Model(&Dog{Model:gorm.Model{ID: 1}}).Preload("Dogs.Info").Preload("Dogs").Association("GirlGods").Find(&girlGod)
// 注意:Preload关联的表查出来的列表数据不能分页,想要分页,只能查关联表,关联表左右join,然后查出来再分页
//查找带条件的关联
//Db.Model(&dog).Where("girl_gods.name = GirlGod01").Association("GirlGods").Find(&girlGod)
Db.Model(&Dog{Model: gorm.Model{ID: 1}}).
//两行下面的where和Order是相对于girl_god表的操作
Where("name in ?", []string{"GirlGod01", "GirlGod02"}).
Order("id desc").
Preload("Dogs.Info", func(db *gorm.DB) *gorm.DB {
return db.Where("money > 100") //Info表
}).
Preload("Dogs", func(db *gorm.DB) *gorm.DB {
return db.Order("id DESC") //dogs列表倒序
}).
Association("GirlGods").Find(&girlGod)
fmt.Println(girlGod)
}
// 修改关联关系
func updateDome01() {
g1 := GirlGod{Model: gorm.Model{ID: 5}}
g2 := GirlGod{Model: gorm.Model{ID: 3}}
dog := Dog{Model: gorm.Model{ID: 3}}
//添加g1和dog3 还有g2和dog3的关系
//Db.Model(&dog).Association("GirlGods").Append(&g1, &g2)
//Db.Model(&dog).Association("GirlGods").Append(&g1)
//Db.Model(&dog).Association("GirlGods").Append(&g2)
//替换关联 会删除所有的关系,然后再插入关系
Db.Model(&dog).Association("GirlGods").Replace(&g1, &g2)
var gs = make([]GirlGod, 0) //空的切片,删除所有关系,不再写入任何关系
dog1 := Dog{Model: gorm.Model{ID: 3}}
Db.Model(&dog1).Association("GirlGods").Replace(&gs)
// 关联计数 Count all GirlGods
//count:=Db.Model(&dog).Association("GirlGods").Count()
//fmt.Println(count)
// 条件计数 Count dog.name in ("GirlGod01", "GirlGod02") all GirlGods
//count:=Db.Model(&dog).Where("name IN ?", []string{"GirlGod01", "GirlGod02"}).Association("GirlGods").Count()
//fmt.Println(count)
//删除g1和dog3的关系
//Db.Model(&dog).Association("GirlGods").Delete(&g1)
//删除dog3的所有关系
//Db.Model(&dog).Association("GirlGods").Clear()
}
// Delete with Select
// 删除记录的同时,也删除他在关联表里的所有的关联关系
// 你也可以在删除记录时通过 Select 来删除具有 has one、has many、many2many 关系的记录,例如:
// 删除god3,同时也删除god3在关联表里的所有的关联关系
func selectDelete() {
//dog3 :=Dog{Model:gorm.Model{ID: 3}}
//Db.Select("GirlGods").Delete(&dog3) //删除dog3的同时,删除和GirlGods的所有关系
//Db.Select("GirlGods","Info").Delete(&dog3) //删除dog3的同时,删除和GirlGods / Info的所有关系
// 删除 dog3 时,也删除用户所有 has one/many、many2many 记录
//Db.Select(clause.Associations).Delete(&dog3)
}
自定义连接表
默认的连接表,只有双方的主键id,展示不了更多信息了
自定义连接表 连接表 可以是一个功能齐全的模型,比如支持 软删除、钩子函数功能,并且可以具有更多字段。您可以通过 SetupJoinTable 设置
package model
import (
"fmt"
"gorm.io/gorm"
"time"
)
//自定义连接表
//默认的连接表,只有双方的主键id,展示不了更多信息了
//自定义连接表 连接表 可以是一个功能齐全的模型,比如支持 软删除、钩子函数功能,并且可以具有更多字段。您可以通过 SetupJoinTable 设置
type Article struct {
ID uint
Title string
Tags []Tag `gorm:"many2many:article_tags"`
}
type Tag struct {
ID uint
Name string
}
type ArticleTag struct {
ArticleID uint `gorm:"primaryKey"`
TagID uint `gorm:"primaryKey"`
CreatedAt time.Time
}
func manyToManyDome03() {
// 设置Article的Tags表为ArticleTag
err := Db.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
if err != nil {
panic(err)
}
// 如果tag要反向应用Article,那么也得加上
err = Db.SetupJoinTable(&Tag{}, "Articles", &ArticleTag{})
if err != nil {
panic(err)
}
Db.AutoMigrate(&Article{}, &Tag{}, &ArticleTag{})
create3()
//replace3()
//getInfo()
}
// BeforeCreate 必须定义好所需的外键,否则会报错
func (p *ArticleTag) BeforeCreate(tx *gorm.DB) (err error) {
//p.CreatedAt = time.Now() // CreatedAt time.Time 由于我们设置的是CreatedAt,gorm会自动填充当前时间,
//p.Sort = 50 // 如果是其他的字段,需要使用到ArticleTag 的添加钩子 BeforeCreate 手动添加
return nil
}
// 创建数据Title和tags 并添加关联数据
func create3() {
Db.Create(&Article{
Title: "flask零基础入门",
Tags: []Tag{
{Name: "python"},
{Name: "后端"},
{Name: "web"},
},
})
}
// 添加文章,关联已有标签
func create3_1() {
Db.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
var tags []Tag
Db.Find(&tags, "name in ?", []string{"python", "web"})
Db.Create(&Article{
Title: "flask请求对象",
Tags: tags,
})
}
// 给已有文章关联标签
func create3_2() {
Db.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
article := Article{
Title: "django基础",
}
Db.Create(&article)
var at Article
var tags []Tag
Db.Find(&tags, "name in ?", []string{"python", "web"})
Db.Take(&at, article.ID).Association("Tags").Append(tags)
}
// 替换已有文章的标签 删除全部关联标签,写入新的标签
func replace3() {
var article Article
var tags []Tag
//Db.Find(&tags, "name in ?", []string{"python1"}) // 查询出python1 如果查不到为空时,会删除全部关系。
Db.Find(&tags, "name in ?", []string{"python"}) // 查询出python1 如果查不到为空时,会删除全部关系。
Db.Take(&article, "title = ?", "flask零基础入门")
Db.Model(&article).Association("Tags").Replace(tags)
}
// 查询文章列表,显示标签
/*
SetupJoinTable
添加和更新的时候得用这个
这样才能走自定义的连接表,以及走它的钩子函数
查询则不需要这个
*/
func getArticleWithTags() {
var articles []Article
Db.Preload("Tags").Find(&articles)
fmt.Println(articles)
}
示列2
// setupJoinTable 自定义连接表
func setupJoinTable() {
// 自定义连接表
//设置Manufacturer的Platforms表为ManufacturerPlatforms
err := DB.SetupJoinTable(&FaAdmin{}, "FaAuthGroups", &FaAuthGroupAccess{})
if err != nil {
logx.Error("db.SetupJoinTable FaAdmin FaAuthGroups table failed", zap.Error(err))
panic("db.SetupJoinTable FaAdmin FaAuthGroups table failed")
}
// 设置Platform的Manufacturers表为ManufacturerPlatforms
err = DB.SetupJoinTable(&FaAuthGroup{}, "FaAdmins", &FaAuthGroupAccess{})
if err != nil {
logx.Error("db.SetupJoinTable FaAuthGroup FaAdmins table failed", zap.Error(err))
panic("db.SetupJoinTable FaAuthGroup FaAdmins table failed")
}
}
重写中间表的外建
// FaAuthGroup 分组表
type FaAuthGroup struct {
ID int64 `json:"id" gorm:"primaryKey;autoIncrement;comment:主键编码"`
ControlTime
Pid int64 `json:"pid" gorm:"pid"` // 父组别
Name string `json:"name" gorm:"name"` // 组名
Rules string `json:"rules" gorm:"rules"` // 规则ID
Status string `json:"status" gorm:"status"` // 状态 hidden 禁用 normal:正常
//多对多关系 fa_auth_group_access为关联表
//joinForeignKey为 FaAuthGroup 表的主键id 关联 中间表fa_auth_group_access中的 group_id
//joinReferences为 faAdmins表的主键 关联 中间表fa_auth_group_access 中的 uid
FaAdmins []FaAdmin `json:"faAdmins" gorm:"comment:管理员;many2many:fa_auth_group_access;joinForeignKey:group_id;JoinReferences:uid"` // 仓库信息
}
数据表
中间表
package models
// FaAuthGroupAccess 权限分组表
type FaAuthGroupAccess struct {
Uid int64 `json:"uid" gorm:"uid"` // 会员ID
FaAdmins []FaAdmin `json:"faAdmins" gorm:"foreignKey:ID;references:uid"`
GroupId int64 `json:"group_id" gorm:"group_id"` // 级别ID
FaAuthGroups []FaAuthGroup `json:"faAuthGroups" gorm:"foreignKey:ID;references:group_id"`
}
// TableName 表名称
func (*FaAuthGroupAccess) TableName() string {
return "fa_auth_group_access"
}
func (t *FaAuthGroupAccess) Create(list []FaAuthGroupAccess) (err error) {
err = DB.Create(&list).Error
return err
}
CREATE TABLE `fa_auth_group_access` (
`uid` int(10) unsigned NOT NULL COMMENT '会员ID',
`group_id` int(10) unsigned NOT NULL COMMENT '级别ID',
UNIQUE KEY `uid_group_id` (`uid`,`group_id`) USING BTREE,
KEY `uid` (`uid`) USING BTREE,
KEY `group_id` (`group_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='权限分组表';
分组表
package models
// FaAuthGroup 分组表
type FaAuthGroup struct {
ID int64 `json:"id" gorm:"primaryKey;autoIncrement;comment:主键编码"`
ControlTime
Pid int64 `json:"pid" gorm:"pid"` // 父组别
Name string `json:"name" gorm:"name"` // 组名
Rules string `json:"rules" gorm:"rules"` // 规则ID
Status string `json:"status" gorm:"status"` // 状态 hidden 禁用 normal:正常
//多对多关系 fa_auth_group_access为关联表
//joinForeignKey为 FaAuthGroup 表的主键id 关联 中间表fa_auth_group_access中的 group_id
//joinReferences为 faAdmins表的主键 关联 中间表fa_auth_group_access 中的 uid
FaAdmins []FaAdmin `json:"faAdmins" gorm:"comment:管理员;many2many:fa_auth_group_access;joinForeignKey:group_id;JoinReferences:uid"` // 仓库信息
}
// TableName 表名称
func (*FaAuthGroup) TableName() string {
return "fa_auth_group"
}
CREATE TABLE `fa_auth_group` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父组别',
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '组名',
`rules` text NOT NULL COMMENT '规则ID',
`createtime` int(10) DEFAULT NULL COMMENT '创建时间',
`updatetime` int(10) DEFAULT NULL COMMENT '更新时间',
`status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='分组表';
package models
import (
"errors"
"ytss_go_zero/app/core/cmd/rpc/pb"
"ytss_go_zero/common/utils"
)
// FaAdmin 管理员表
type FaAdmin struct {
ID int64 `json:"id" gorm:"primaryKey;autoIncrement;comment:主键编码"`
ControlTime
Username string `json:"username" gorm:"username"` // 用户名
Nickname string `json:"nickname" gorm:"nickname"` // 昵称
Password string `json:"password" gorm:"password"` // 密码
Salt string `json:"salt" gorm:"salt"` // 密码盐
Department string `json:"department" gorm:"column:department"` // 部门
Avatar string `json:"avatar" gorm:"avatar"` // 头像
Email string `json:"email" gorm:"email"` // 电子邮箱
Loginfailure int8 `json:"loginfailure" gorm:"loginfailure"` // 失败次数
Logintime int64 `json:"logintime" gorm:"logintime"` // 登录时间
Loginip string `json:"loginip" gorm:"loginip"` // 登录IP
Token string `json:"token" gorm:"token"` // Session标识
Status string `json:"status" gorm:"status"` // 状态 hidden 禁用 normal:正常
//多对多关系 fa_auth_group_access为关联表
//joinForeignKey为 FaAdmin表的主键id 关联 中间表fa_auth_group_access中的uid
//joinReferences为 FaAuthGroup 关联 中间表fa_auth_group_access 中的 group_id
FaAuthGroups []FaAuthGroup `json:"faAuthGroups" gorm:"comment:归属角色;many2many:fa_auth_group_access;joinForeignKey:uid;JoinReferences:group_id;"` // 仓库信息
}
// TableName 表名称
func (*FaAdmin) TableName() string {
return "fa_admin"
}
CREATE TABLE `fa_admin` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`username` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
`nickname` varchar(50) NOT NULL DEFAULT '' COMMENT '昵称',
`password` varchar(32) NOT NULL DEFAULT '' COMMENT '密码',
`salt` varchar(30) NOT NULL DEFAULT '' COMMENT '密码盐',
`department` varchar(50) DEFAULT NULL COMMENT '部门',
`avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '头像',
`email` varchar(100) NOT NULL DEFAULT '' COMMENT '电子邮箱',
`loginfailure` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '失败次数',
`logintime` int(10) DEFAULT NULL COMMENT '登录时间',
`loginip` varchar(50) DEFAULT NULL COMMENT '登录IP',
`createtime` int(10) DEFAULT NULL COMMENT '创建时间',
`updatetime` int(10) DEFAULT NULL COMMENT '更新时间',
`token` varchar(59) NOT NULL DEFAULT '' COMMENT 'Session标识',
`status` varchar(30) NOT NULL DEFAULT 'normal' COMMENT '状态',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `username` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='管理员表';
作者:海马 创建时间:2024-07-09 09:11
最后编辑:海马 更新时间:2024-12-22 19:32
最后编辑:海马 更新时间:2024-12-22 19:32