gorm 关系一对一,一对多,多对多查询

gorm v2版本

Belongs To

mysql表

CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL DEFAULT '',
`c_sn` int(11) NOT NULL DEFAULT '0',
`created_at` datetime(3) DEFAULT NULL,
`updated_at` datetime(3) DEFAULT NULL,
`deleted_at` datetime(3) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `index_casusers_on_cas_uid` (`cas_uid`) USING BTREE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `company` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL DEFAULT '',
`c_sn` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE,
KEY `index_c_sn` (`c_sn`) USING BTREE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

belongs to 会与另一个模型建立了一对一的连接。 这种模型的每一个实例都 “属于” 另一个模型的一个实例。

例如,您的应用包含 user 和 company,并且每个 user 都可以分配给一个 company

主表与其它表建立关连(关链关系字段存到主表)

// `User` 属于 `Company`,`CompanyID` 是外键
type User struct {
gorm.Model
Name string
CompanyID int
Company Company
} type Company struct {
ID int
Name string
}

重写外键: foreignKey:company_sn (指定user表里的company_sn)

User表外键默认关联Company的主键id ,可以重写成其它外键,如Company里的c_sn字段

重写引用: references:c_sn (指定company_表里的c_sn)

指定user表里的company_sn字段关联company_表里的c_sn

  1. 写法一:和mysql里的字段字写成一样的

    gorm:"foreignKey:company_sn;references:c_sn"

  2. 写法一:和结构体里key名写成一样的

    gorm:"foreignKey:CompanySn;references:CSn"

// `User` 属于 `Company`,`CompanyID` 是外键
type User struct {
gorm.Model
Name string string `json:"name"`
CompanySn string `json:"company_sn"` //关连接company表
Company Company `json:"casusers" gorm:"foreignKey:company_sn;references:c_sn"`
} type Company struct {
ID int `json:"ID"`
Name string `json:"name"`
CSn string `json:"cSn"`
} // 查找 user 时预加载相关 Company
db.Joins("Company").First(&user, 1)
// 查找 user 时预加载相关 Company
db.Preload("Company").Find(&users)

Has One

mysql表

CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL DEFAULT '',
`created_at` datetime(3) DEFAULT NULL,
`updated_at` datetime(3) DEFAULT NULL,
`deleted_at` datetime(3) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `credit_card ` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`number` varchar(64) NOT NULL DEFAULT '',
`user_name` varchar(64) NOT NULL DEFAULT '',
`created_at` datetime(3) DEFAULT NULL,
`updated_at` datetime(3) DEFAULT NULL,
`deleted_at` datetime(3) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `index_number` (`number`) USING BTREE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

has one 与另一个模型建立一对一的关联,但它和一对一关系有些许不同。 这种关联表明一个模型的每个实例都包含或拥有另一个模型的一个实例。

其它表与主表建立关连(关链关系字段存到其它表中) 与Belongs to正好相反

例如,您的应用包含 user 和 credit card 模型,且每个 user 只能有一张 credit card。

// User 有一张 CreditCard,CreditCardID 是外键

type User struct {
gorm.Model
CreditCard CreditCard
} type CreditCard struct {
gorm.Model
Number string
UserID uint
}

重写外键

对于 has one 关系,同样必须存在外键字段。拥有者将把属于它的模型的主键保存到这个字段。

这个字段的名称通常由 has one 模型的类型加上其 主键 生成,对于上面的例子,它是 UserID

user 添加 credit card 时,它会将 userID 保存到自己的 UserID字段。

如果你想要使用另一个字段来保存该关系,你同样可以使用标签 foreignKey 来更改它,例如:

type User struct {
gorm.Model
CreditCard CreditCard `gorm:"foreignKey:UserName"`
// 使用 UserName 作为外键 关联到user表的id上
} type CreditCard struct {
gorm.Model
Number string
UserName string
}

重写引用

默认情况下,拥有者实体会将 has one 对应模型的主键保存为外键,您也可以修改它,用另一个字段来保存,例如下个这个使用 Name 来保存的例子。

您可以使用标签 references 来更改它,例如:

CreditCard表里的 UserName 关联到User表里的 name字段上

CreditCard CreditCard gorm:"foreignkey:UserName;references:name"

type User struct {
gorm.Model
Name string `sql:"index"`
CreditCard CreditCard `gorm:"foreignkey:UserName;references:name"`
} type CreditCard struct {
gorm.Model
Number string
UserName string
} // 查找 user 时预加载相关 Company
db.Joins("CreditCard").First(&user, 1)
// 查找 user 时预加载相关 Company
db.Preload("CreditCard").Find(&users)

Has Many

has many 与另一个模型建立了一对多的连接。 不同于 has one,拥有者可以有零或多个关联模型。

例如,您的应用包含 user 和 credit card 模型,且每个 user 可以有多张 credit card。

/ User 有多张 CreditCard,UserID 是外键
type User struct {
gorm.Model
CreditCards []CreditCard
} type CreditCard struct {
gorm.Model
Number string
UserID uint
}

重写外键

要定义 has many 关系,同样必须存在外键。 默认的外键名是拥有者的类型名加上其主键字段名

例如,要定义一个属于 User 的模型,则其外键应该是 UserID

此外,想要使用另一个字段作为外键,您可以使用 foreignKey 标签自定义它:

type User struct {
gorm.Model
CreditCards []CreditCard `gorm:"foreignKey:UserRefer"`
} type CreditCard struct {
gorm.Model
Number string
UserRefer uint
}

重写引用

GORM 通常使用拥有者的主键作为外键的值。 对于上面的例子,它是 UserID 字段。

为 user 添加 credit card 时,GORM 会将 user 的 ID 字段保存到 credit card 的 UserID 字段。

同样的,您也可以使用标签 references 来更改它,例如:

type User struct {
gorm.Model
MemberNumber string
CreditCards []CreditCard `gorm:"foreignKey:UserNumber;references:MemberNumber"`
} type CreditCard struct {
gorm.Model
Number string
UserNumber string
}

多态关联

GORM 为 has onehas many 提供了多态关联支持,它会将拥有者实体的表名、主键都保存到多态类型的字段中。

type Dog struct {
ID int
Name string
Toys []Toy `gorm:"polymorphic:Owner;"`
} type Toy struct {
ID int
Name string
OwnerID int
OwnerType string
} db.Create(&Dog{Name: "dog1", Toy: []Toy{{Name: "toy1"}, {Name: "toy2"}}})
// INSERT INTO `dogs` (`name`) VALUES ("dog1")
// INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","dogs"), ("toy2","1","dogs")

您可以使用标签 polymorphicValue 来更改多态类型的值,例如:

type Dog struct {
ID int
Name string
Toys []Toy `gorm:"polymorphic:Owner;polymorphicValue:master"`
} type Toy struct {
ID int
Name string
OwnerID int
OwnerType string
} db.Create(&Dog{Name: "dog1", Toys: []Toy{{Name: "toy1"}, {Name: "toy2"}}})
// INSERT INTO `dogs` (`name`) VALUES ("dog1")
// INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","master"), ("toy2","1","master")

Has Many 的 CURD

查看 关联模式 获取 has many 相关的用法

预加载

GORM 可以通过 Preload 预加载 has many 关联的记录,查看 预加载 获取详情

自引用 Has Many

type User struct {
gorm.Model
Name string
ManagerID *uint
Team []User `gorm:"foreignkey:ManagerID"`
}

外键约束

你可以通过为标签 constraint 配置 OnUpdateOnDelete 实现外键约束,在使用 GORM 进行迁移时它会被创建,例如:

type User struct {
gorm.Model
CreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
} type CreditCard struct {
gorm.Model
Number string
UserID uint
}

你也可以在删除记录时通过 Select 来删除 has many 关联的记录,查看 Delete with Select 获取详情

Many To Many

Many to Many 会在两个 model 中添加一张连接表。

例如,您的应用包含了 user 和 language,且一个 user 可以说多种 language,多个 user 也可以说一种 language。

// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_languages;"`
} type Language struct {
gorm.Model
Name string
}

当使用 GORM 的 AutoMigrateUser 创建表时,GORM 会自动创建连接表

反向引用

// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {
gorm.Model
Languages []*Language `gorm:"many2many:user_languages;"`
} type Language struct {
gorm.Model
Name string
Users []*User `gorm:"many2many:user_languages;"`
}

重写外键

对于 many2many 关系,连接表会同时拥有两个模型的外键,例如:

type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_languages;"`
} type Language struct {
gorm.Model
Name string
} // Join Table: user_languages
// foreign key: user_id, reference: users.id
// foreign key: language_id, reference: languages.id

若要重写它们,可以使用标签 foreignKeyreferencesjoinforeignKeyjoinReferences。当然,您不需要使用全部的标签,你可以仅使用其中的一个重写部分的外键、引用。

type User struct {
gorm.Model
Profiles []Profile `gorm:"many2many:user_profiles;foreignKey:Refer;joinForeignKey:UserReferID;References:UserRefer;JoinReferences:UserRefer"`
Refer uint `gorm:"index:,unique"`
} type Profile struct {
gorm.Model
Name string
UserRefer uint `gorm:"index:,unique"`
} // 这会创建连接表:user_profiles
// 外键:user_refer_id,,引用:users.refer
// 外键:profile_refer,引用:profiles.user_refer

注意: 某些数据库只允许在唯一索引字段上创建外键,如果您在迁移时会创建外键,则需要指定 unique index 标签。

自引用 Many2Many

自引用 many2many 关系

type User struct {
gorm.Model
Friends []*User `gorm:"many2many:user_friends"`
} // 会创建连接表:user_friends
// foreign key: user_id, reference: users.id
// foreign key: friend_id, reference: users.id

预加载

GORM 可以通过 Preload 预加载 has many 关联的记录,查看 预加载 获取详情

Many2Many 的 CURD

查看 关联模式 获取 many2many 相关的用法

自定义连接表

连接表 可以是一个全功能的模型,支持 Soft Delete钩子、更多的字段,就跟其它模型一样。您可以通过 SetupJoinTable 指定它,例如:

注意: 自定义连接表要求外键是复合主键或复合唯一索引

type Person struct {
ID int
Name string
Addresses []Address `gorm:"many2many:person_addresses;"`
} type Address struct {
ID uint
Name string
} type PersonAddress struct {
PersonID int `gorm:"primaryKey"`
AddressID int `gorm:"primaryKey"`
CreatedAt time.Time
DeletedAt gorm.DeletedAt
} func (PersonAddress) BeforeCreate(db *gorm.DB) error {
// ...
} // Change model Person's field Addresses' join table to PersonAddress
// PersonAddress must defined all required foreign keys or it will raise error
err := db.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})

外键约束

你可以通过为标签 constraint 配置 OnUpdateOnDelete 实现外键约束,在使用 GORM 进行迁移时它会被创建,例如:

type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_speaks;"`
} type Language struct {
Code string `gorm:"primarykey"`
Name string
} // CREATE TABLE `user_speaks` (`user_id` integer,`language_code` text,PRIMARY KEY (`user_id`,`language_code`),CONSTRAINT `fk_user_speaks_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE,CONSTRAINT `fk_user_speaks_language` FOREIGN KEY (`language_code`) REFERENCES `languages`(`code`) ON DELETE SET NULL ON UPDATE CASCADE);

你也可以在删除记录时通过 Select 来删除 many2many 关系的记录,查看 Delete with Select 获取详情

复合外键

如果您的模型使用了 复合主键,GORM 会默认启用复合外键。

您也可以覆盖默认的外键、指定多个外键,只需用逗号分隔那些键名,例如:

type Tag struct {
ID uint `gorm:"primaryKey"`
Locale string `gorm:"primaryKey"`
Value string
} type Blog struct {
ID uint `gorm:"primaryKey"`
Locale string `gorm:"primaryKey"`
Subject string
Body string
Tags []Tag `gorm:"many2many:blog_tags;"`
LocaleTags []Tag `gorm:"many2many:locale_blog_tags;ForeignKey:id,locale;References:id"`
SharedTags []Tag `gorm:"many2many:shared_blog_tags;ForeignKey:id;References:id"`
} // 连接表:blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: blog_locale, reference: blogs.locale
// foreign key: tag_id, reference: tags.id
// foreign key: tag_locale, reference: tags.locale // 连接表:locale_blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: blog_locale, reference: blogs.locale
// foreign key: tag_id, reference: tags.id // 连接表:shared_blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: tag_id, reference: tags.id

查看 复合主键 获取详情

预加载

GORM 允许在 Preload 的其它 SQL 中直接加载关系,例如:


type User struct {
gorm.Model
Name string
CompanyID uint
Company Company
Roles Roles
Orders []Order
} type Order struct {
gorm.Model
UserID uint
Price float64
} // 查找 user 时预加载相关 Order
db.Preload("Company").Find(&users)
// SELECT * FROM users;
// SELECT * FROM company WHERE user_id IN (1,2,3,4); db.Preload("Orders").Preload("Company").Preload("Role").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
// SELECT * FROM company WHERE user_id IN (1,2,3,4); // has one
// SELECT * FROM roles WHERE id IN (4,5,6); // belongs to

Joins 预加载

Preload 在一个单独查询中加载关联数据。而 Join Preload 会使用 inner join 加载关联数据,例如:


db.Joins("Company").Joins("Manager").Joins("Account").First(&user, 1)
db.Joins("Company").Joins("Manager").Joins("Account").First(&user, "users.name = ?", "jinzhu")
db.Joins("Company").Joins("Manager").Joins("Account").Find(&users, "users.id IN ?", []int{1,2,3,4,5})

注意 Join Preload 适用于一对一的关系,例如: has one, belongs to

预加载全部

与创建、更新时使用 Select 类似,clause.Associations 也可以和 Preload 一起使用,它可以用来 预加载 全部关联,例如:

type User struct {
gorm.Model
Name string
CompanyID uint
Company Company
Role Role
Orders []Order
} db.Preload(clause.Associations).Find(&users)

clause.Associations 不会预加载嵌套的关联,但你可以使用嵌套预加载 例如:

db.Preload("Orders.OrderItems.Product").Preload(clause.Associations).Find(&users)

带条件的预加载

GORM 允许带条件的 Preload 关联,类似于内联条件

// 带条件的预加载 Order

db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)

// SELECT * FROM users;

// SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled');

db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)

// SELECT * FROM users WHERE state = 'active';

// SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled');

自定义预加载 SQL

您可以通过 func(db *gorm.DB) *gorm.DB 实现自定义预加载 SQL,例如:

db.Preload("Orders", func(db *gorm.DB) *gorm.DB {

  return db.Order("orders.amount DESC")

}).Find(&users)

// SELECT * FROM users;

// SELECT * FROM orders WHERE user_id IN (1,2,3,4) order by orders.amount DESC;

嵌套预加载

GORM 支持嵌套预加载,例如:

db.Preload("Orders.OrderItems.Product").Preload("CreditCard").Find(&users)

// 自定义预加载 Orders 的条件

// 这样,GORM 就不会加载不匹配的 order 记录

db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users)

gorm 关系一对一,一对多,多对多查询的更多相关文章

  1. Python进阶----表与表之间的关系(一对一,一对多,多对多),增删改查操作

    Python进阶----表与表之间的关系(一对一,一对多,多对多),增删改查操作,单表查询,多表查询 一丶表与表之间的关系 背景: ​ ​ ​  ​ ​ 由于如果只使用一张表存储所有的数据,就会操作数 ...

  2. JPA级联(一对一 一对多 多对多)注解【实际项目中摘取的】并非自己实际应用

    下面把项目中的用户类中有个:一对一  一对多  多对多的注解对应关系列取出来用于学习      说明:项目运行正常 问题类:一对多.一对一.多对多 ============一对多 一方的设置 @One ...

  3. mybatis 一对一 一对多 多对多

    一对一 一对多 多对多

  4. day 69-70 一对一 一对多 多对一联表查询

    day 69 orm操作之表关系,多对多,多对一 多对一/一对多, 多对多{类中的定义方法} day69 1. 昨日内容回顾 1. 单表增删改查 2. 单表查询API 返回QuerySet对象的: 1 ...

  5. Django ORM 一对一,一对多,多对多, 添加,批量插入和查询

    模型类 class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_ ...

  6. 使用NHibernate(7)-- 一对一 && 一对多 && 多对多

    1, 一对一. 对于数据量比较大的时候,考虑查询的性能,肯能会把一个对象的属性分到两个表中存放:比如用户和用户资料,经常使用的一般是Id和用户名,用户资料(学校,籍贯等)是不经常被查询的,所以就会分成 ...

  7. JPA 一对一 一对多 多对一 多对多配置

    1 JPA概述 1.1 JPA是什么 JPA (Java Persistence API) Java持久化API.是一套Sun公司 Java官方制定的ORM 方案,是规范,是标准 ,sun公司自己并没 ...

  8. JAVA日记之mybatis-3一对一,一对多,多对多xml与注解配置

    1.Mybatis多表查询1.1 一对一查询1.1.1 一对一查询的模型用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户 一对一查询的需求:查询一个订单,与此同时查询出该订单所属的 ...

  9. SQLAlchemy_定义(一对一/一对多/多对多)关系

    目录 Basic Relationship Patterns One To Many One To One Many To Many Basic Relationship Patterns 基本关系模 ...

  10. MySQL一对一:一对多:多对多: 实例!!!!

    学生表和课程表可以多对多 一个学生可以学多门课程 一门课程可以有多个学生: 多对多 *** 一个学生对应一个班级 一个班级对应多个学生: 一对多 *** 一个老师对应多个学生 多个学生对应一个老师:一 ...

随机推荐

  1. KingbaseES大数据量分区表添加主键与索引

    KingbaseES大数据量分区表添加主键与索引 一.环境信息: 系统信息: $ cat /etc/centos-release CentOS Linux release 8.2.2004 (Core ...

  2. NOIP 2007 普及组

    NOIP 2007 普及组(DONE) 注:本文不附原题,可上Luogu有题对照查询. 1.CPU:即中央处理器,由运算器+控制器+寄存器组成,不含主板(但CPU是装在主板上的). 2.二维表即常见的 ...

  3. CSS+HTML+flexible.js+rem实现屏幕缩放适配概念原理解释

    首先理解几个概念: (1)屏幕尺寸:屏幕对角线的长度,一般用英寸表示,1英寸=2.54cm. (2)dp((或者叫dip):设备独立像素,也就是设备屏幕上多少个点. (3)dpi:印刷行业术语,像素密 ...

  4. 链表队列(LinkedListQueue)

    栈操作 入队 template<typename T> void LinkedListQueue<T>::enqueue(T e) { if (tail == nullptr) ...

  5. 网络组件axios可以在OpenHarmony上使用了

    什么是axios 上古浏览器页面在向服务器请求数据时,因为返回的是整个页面的数据,页面都会强制刷新一下,这对于用户来讲并不是很友好.并且我们只是需要修改页面的部分数据,但是从服务器端发送的却是整个页面 ...

  6. Java 数据类型详解与类型转换技巧

    Java 数据类型 Java 中的变量必须是指定的数据类型: int myNum = 5; // 整数 float myFloatNum = 5.99f; // 浮点数 char myLetter = ...

  7. 华为3D建模服务(3D Modeling Kit),轻松构建高质量3D模型

    华为3D建模服务(3D Modeling Kit)是华为在图形图像领域又一技术开放,面向有3D模型.动画制作等能力诉求的应用开发者,基于AI技术,提供3D物体模型自动生成和PBR材质生成功能,实现3D ...

  8. spring boot 手动value和自动注入配置的区别[五]

    前言 前面两篇中,写道我们注入配置的方式,是通过是注解的方式完成,如下: @ConfigurationProperties(prefix ="person") 这意味着: 我们写一 ...

  9. mysql 重新整理——性能下降的原因[四]

    前言 什么是性能下降? 其实就是代码运行的环境变了,那么环境变化是什么? 比如cpu上升了,内存满了.有或者表中数量增加了,量变了. 其实这些是dba干的,但是呢,我们也需要去了解下,并且优化我们的c ...

  10. vue+mockjs模拟用户登录接口(高仿书旗)

    项目demo:http://39.103.131.74:8888/shuqi