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 sys_bulkload数据加载工具错误处理

    一.关于sys_bulkload数据加载工具 sys_bulkload是KingbaseES提供的快速加载数据的命令行工具.用户使用sys_bulkload工具能够把一定格式的文本数据简单.快速的加载 ...

  2. KingbaseES变更表结构表重写问题

    在实际项目使用数据库的过程中修改字段类型这类需求比较常见. 一.修改表字段类型需要知道: 1.修改表结构可能会导致表进行重写(表OID发生变化). 2.修改表结构带有索引或者字段类型长度或者精度操作时 ...

  3. KingbaseES V8R6 最老事务阻止vacuum freeze

    前言 最近生产环境发生几次由于长事务导致表.库年龄没法回收的情况.我们要规避这种情况的发生,不要等发生了再去强制中断会话连接. 当数据库中存在最老事务版本xmin,那么早于他的快照可以被标记为froz ...

  4. 前端 Typescript 入门

    前端 Typescript 入门 Ant design vue4.x 基于 vue3,示例默认是 TypeScript.比如 table 组件管理. vue3 官网介绍也使用了 TypeScript, ...

  5. .Net Core AutoFac 使用方法讲解大全,具体详细使用知识总结

    AutoFac 具体使用知识总结 阅读前提示 AutoFac 只是众多IOC框架的其中一种, 比较主流的有Unity.autofac.spring.net.MEF.Injection.Asp.Net ...

  6. #NTT,原根#洛谷 3321 JZOJ 4051 [SDOI2015]序列统计

    题目 分析 首先朴素dp方程 设\(dp[i][j]\)表示\(i\)个数的数列乘积为\(j\)的方案 那么\(dp[i][j*a[k]\bmod m]=itself+dp[i-1][j]\) 这可以 ...

  7. #ST表,单调栈#洛谷 5648 Mivik的神力

    题目 分析 考虑答案应该是一段单调不下降的序列, 考虑预处理出每个点往后第一个大于这个点的位置, 那么答案应该是左端点到区间内最大的位置以及这个位置到右端点的贡献 那么区间最大的位置可以用ST表做,然 ...

  8. 掌握 C++ 编译过程:面试中常见问题解析

    C++是一种高级编程语言,但是计算机并不能直接理解它.因此,需要将C++代码翻译成计算机可以理解的机器语言.这个过程就是编译过程,是C++程序从源代码到可执行文件的转换过程,包括预处理.编译.汇编和链 ...

  9. WPF/MVVM模式入门教程(一):简介与规范

    引用:https://www.cnblogs.com/flh1/p/12421652.html 什么是MVVM模式? MVVM的全称是--Model.View.ViewModel,翻译过来就是:模型. ...

  10. Python生成唯一ID----UUID

    # UUID 生成唯一ID # uuid 是Python内置模块,主要有五种算法. import uuid # uuid1() 基于时间戳 a1 = uuid.uuid1() print('uuid1 ...