Golang GORM系列:定义GORM模型及关系指南

devtools/2025/2/14 2:13:37/

使用GORM进行数据库管理的核心是定义模型的技能。模型是程序的面向对象结构和数据库的关系世界之间的纽带。本文深入研究了在GORM中创建成功模型的艺术,研究了如何设计结构化的Go结构,用标记注释字段,以及开发跨模型的链接,以便最大限度地发挥应用程序数据库交互的潜力。

在这里插入图片描述

在GORM中创建结构模型

良好定义的结构模型是基于gorm的应用程序的大脑。结构模型中的每个字段对应于数据库表中的一列,该列由结构模型表示。结构模型是这样制作的:

package modelsimport ("gorm.io/gorm"
)type User struct {gorm.ModelName  stringEmail string `gorm:"uniqueIndex"`Age   int
}

上面示例中, User struct 模型定义数据库表的列: ID, CreatedAt, UpdatedAt, DeletedAt, Name, Email, Age.

为字段映射添加标签

GORM依赖于结构体标记将结构体字段映射到数据库列。标签提供指导GORM进行数据库操作的元数据。常见标签包括:

  • gorm:"primaryKey": 标记字段为 primary key.
  • gorm:"uniqueIndex": 定义字段为唯一索引.
  • gorm:"not null": 标记字段不能为空.
  • gorm:"column:custom_name": 映射字段为自定义的名称.
type Product struct {gorm.ModelName     stringPrice    float64Category string `gorm:"column:item_category"`
}

在这个例子中,“Category”字段被映射到“item_category”列。

模型关联和关系

GORM擅长对表之间的复杂关系进行建模。关联定义了不同的模型如何相互关联,使您能够轻松地获取相关数据。

一对一的关系:

type User struct {gorm.ModelProfile Profile
}type Profile struct {gorm.ModelUserID  uintAddress string
}

在这个例子中,一个“User”对应一个“Profile”。‘ Profile ’结构中的‘ UserID ’字段用作外键。

一对多的关系:

type User struct {gorm.ModelOrders []Order
}type Order struct {gorm.ModelUserID  uintProduct string
}

在这里,一个“User”可以有多个“Order”,每个订单都通过“UserID”外键与用户相关联。

多对多关系:

// User 定义用户模型
type User struct {gorm.ModelName  stringRoles []Role `gorm:"many2many:user_roles;"`
}
// Role 定义角色模型
type Role struct {gorm.ModelName  stringUsers []User `gorm:"many2many:user_roles;"`
}

这个例子演示了“User”和“Role”模型之间的多对多关系。GORM处理中间表user_roles的创建。

在查询中使用关联

关联简化了对相关数据的查询。例如,获取用户的订单:

var user User
db.Preload("Orders").Find(&user, 1)

‘ Preload ’方法在查询结果中急切地加载用户的订单。

完整示例

多对多关系表示一个实体的多个实例可以关联到另一个实体的多个实例。在用户和角色的场景中,一个用户可以拥有多个角色,一个角色也可以被多个用户拥有。在数据库层面,通常需要一个中间表来维护这种关系。

1. 项目初始化

首先,确保你已经安装了 Go 和 GORM 库。可以使用以下命令初始化一个新的 Go 模块并安装 GORM:

go mod init example.com/user-role
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
2. 代码实现
package mainimport ("gorm.io/driver/sqlite""gorm.io/gorm"
)// User 定义用户模型
type User struct {gorm.ModelName  stringRoles []Role `gorm:"many2many:user_roles;"`
}// Role 定义角色模型
type Role struct {gorm.ModelName  stringUsers []User `gorm:"many2many:user_roles;"`
}func main() {// 连接数据库db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})if err != nil {panic("failed to connect database")}// 自动迁移模型db.AutoMigrate(&User{}, &Role{})// 创建角色adminRole := Role{Name: "Admin"}userRole := Role{Name: "User"}db.Create(&adminRole)db.Create(&userRole)// 创建用户user1 := User{Name: "Alice", Roles: []Role{adminRole, userRole}}user2 := User{Name: "Bob", Roles: []Role{userRole}}db.Create(&user1)db.Create(&user2)// 查询用户及其角色var users []Userdb.Preload("Roles").Find(&users)for _, user := range users {printUserWithRoles(user)}
}// printUserWithRoles 打印用户及其角色信息
func printUserWithRoles(user User) {print("User: ", user.Name, ", Roles: ")for _, role := range user.Roles {print(role.Name, " ")}println()
}
3. 代码解释
  • 模型定义
    • User 结构体和 Role 结构体分别表示用户和角色模型。每个结构体都嵌入了 gorm.Model,这是 GORM 提供的基础模型,包含 IDCreatedAtUpdatedAtDeletedAt 字段。
    • Roles 字段在 User 结构体中,Users 字段在 Role 结构体中,它们通过 gorm:"many2many:user_roles;" 标签指定了多对多关系,中间表名为 user_roles
  • 数据库操作
    • 使用 gorm.Open 方法连接到 SQLite 数据库。
    • db.AutoMigrate 方法用于自动创建数据库表结构。
    • 创建角色和用户,并将角色关联到用户。
    • 使用 db.Preload("Roles").Find(&users) 方法查询用户及其关联的角色。Preload("Roles"):这是 GORM 提供的预加载功能。预加载的目的是在查询主模型(这里是 User 模型)的同时,也将关联的模型(这里是 Role 模型)的数据一起查询出来,避免了 N + 1 查询问题。具体来说,Preload("Roles") 告诉 GORM 在查询用户时,同时查询每个用户关联的角色,并将这些角色数据填充到 User 结构体的 Roles 字段中。
  • 打印信息
    • printUserWithRoles 函数用于打印用户及其角色的信息。
4. 运行代码

将上述代码保存为 main.go,然后在终端中运行:

go run main.go

运行后,你将看到输出的用户及其角色信息,这表明多对多关系已经成功建立和查询。

5. 关联表增加额外字段

当需要给中间表 user_roles 增加额外字段(如 status)时,就不能单纯依靠 GORM 的 many2many 标签来处理多对多关系了,而是要手动定义中间表的模型,然后通过 has many 和 belongs to 关系来建立用户、角色和中间表之间的关联。以下是修改后的完整示例:

package mainimport ("gorm.io/driver/sqlite""gorm.io/gorm"
)// User 定义用户模型
type User struct {gorm.ModelName       stringUserRoles  []UserRoleRoles      []Role `gorm:"many2many:user_roles;foreignKey:ID;joinForeignKey:UserID;References:ID;joinReferences:RoleID"`
}// Role 定义角色模型
type Role struct {gorm.ModelName       stringUserRoles  []UserRoleUsers      []User `gorm:"many2many:user_roles;foreignKey:ID;joinForeignKey:RoleID;References:ID;joinReferences:UserID"`
}// UserRole 定义中间表模型
type UserRole struct {gorm.ModelUserID uintRoleID uintStatus string // 新增的额外字段
}func main() {// 连接数据库db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})if err != nil {panic("failed to connect database")}// 自动迁移模型db.AutoMigrate(&User{}, &Role{}, &UserRole{})// 创建角色adminRole := Role{Name: "Admin"}userRole := Role{Name: "User"}db.Create(&adminRole)db.Create(&userRole)// 创建用户user1 := User{Name: "Alice"}db.Create(&user1)// 关联用户和角色,并设置中间表的额外字段userRole1 := UserRole{UserID: user1.ID,RoleID: adminRole.ID,Status: "Active",}userRole2 := UserRole{UserID: user1.ID,RoleID: userRole.ID,Status: "Inactive",}db.Create(&userRole1)db.Create(&userRole2)// 查询用户及其角色var users []Userdb.Preload("UserRoles.Role").Find(&users)for _, user := range users {printUserWithRoles(user)}
}// printUserWithRoles 打印用户及其角色信息
func printUserWithRoles(user User) {print("User: ", user.Name, ", Roles: ")for _, userRole := range user.UserRoles {print(userRole.Role.Name, " (Status: ", userRole.Status, ") ")}println()
}
  • UserRole 结构体:这是手动定义的中间表模型,包含 UserIDRoleID 用于关联用户和角色,还新增了 Status 字段作为额外字段。
  • 关联用户和角色并设置额外字段:通过创建 UserRole 实例并设置 UserIDRoleIDStatus 字段,然后将其保存到数据库,完成用户和角色的关联并设置中间表的额外信息。

最后总结

在应用程序中高效管理数据库的关键是在GORM中定义模型。通过创建模型之间的关系、用相关标记注释字段以及构建有组织的结构模型,你为平滑的数据库交互构建了坚实的基础。GORM中的一对一、一对多和多对多关系处理使您能够轻松地为复杂的数据情况建模。结构良好的基础会产生可伸缩和可维护的应用程序,使你的数据库管理之旅变得简单而富有成效。


http://www.ppmy.cn/devtools/158642.html

相关文章

Django学习笔记(第一天:Django基本知识简介与启动)

博主毕业已经工作一年多了,最基本的测试工作已经完全掌握。一方面为了解决当前公司没有自动化测试平台的痛点,另一方面为了向更高级的测试架构师转型,于是重温Django的知识,用于后期搭建测试自动化平台。 为什么不选择Java&#x…

在 rtthread中,rt_list_entry (rt_container_of) 已知结构体成员的地址,反推出结构体的首地址

rt_list_entry (rt_container_of)宏定义: /*** rt_container_of - return the start address of struct type, while ptr is the* member of struct type.*/ #define rt_container_of(ptr, type, member) \((type *)((char *)(ptr) - (unsigned long)(&((type *…

【已解决】lxml.etree.ParserError: Document is empty

本专栏解决日常生活工作中非快速找到解决方案的问题。 问题背景 在爬取某网站时,使用开源框架报错:lxml.etree.ParserError: Document is empty 解决方案 1、多个搜索引擎中查找,建议都是对lxml的python源码进行修改,不好用。…

npm包管理工具

包管理工具 npm 包管理工具 介绍 Node Package Manager:也就是Node包管理工具但是目前已经不仅仅是Node包管理器,在前端项目中我们也使用它来管理依赖的包比如 vue、vue-router、vuex、express、koa 下载和安装 npm属于Node的管理工具,安…

【Apache Paimon】-- 作为一名小白,如何系统地学习 Apache paimon?

目录 一、整体规划 1. 了解基本概念与背景 2. 学习资料的选择 3. 学习路径与规划 4. 学习建议 5. 注意事项 6. 参考学习资料 二、详细计划 阶段 1:了解基础(1-2 周) 阶段 2:深入掌握核心功能(3-4 周&#xf…

【DeepSeek】在本地计算机上部署DeepSeek-R1大模型实战(完整版)

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈人工智能与大模型应用 ⌋ ⌋ ⌋ 人工智能(AI)通过算法模拟人类智能,利用机器学习、深度学习等技术驱动医疗、金融等领域的智能化。大模型是千亿参数的深度神经网络(如ChatGPT&…

springboot的http请求不通原因

Spring Boot 应用中 HTTP 请求不通可能由多种原因引起,以下是一些常见问题及解决方法: 1. 端口冲突 问题:应用端口被占用。 解决:检查端口占用情况,修改 application.properties 或 application.yml 中的端口配置。 …

ADB的安装和使用

文章目录 前言一、ADB是什么?一、ADB的基本概念二、ADB的主要功能三、ADB在Linux系统中的安装与使用四、ADB命令的示例 二、windows 安装ADB1.ADB不用安装,解压文件后添加环境变量即可2.测试是否能使用 三、与linux通信3.1 将 usb设备连接到虚拟机3.2.测…