Go GORM介绍

news2024/11/18 13:43:10

GORM 是一个功能强大的 Go 语言 ORM(对象关系映射)库,它提供了一种方便的方式来与 SQL 数据库进行交互,而不需要编写大量的 SQL 代码。

GORM的关键特性

  • 全功能的ORM:支持几乎所有的ORM功能,包括模型定义、基本的CRUD操作、复杂查询、关联处理等。

  • 关联支持:非常灵活的关联(has one, has many, belongs to, many to many, polymorphism, single-table inheritance)功能。

  • 钩子(Hooks):支持在create/save/update/delete/find操作前后进行自定义处理。

  • 预加载(Eager Loading):使用Preload, Joins等方式预加载关联数据。

  • 事务处理:支持事务、嵌套事务、保存点以及回滚到保存点。

  • 上下文支持: 支持上下文管理、准备语句模式、DryRun模式。

  • 批量操作: 支持批量插入、分批次查询、通过Map进行查找/创建、使用SQL Expr和Context Valuer进行CRUD。

  • SQL构建器: 支持Upsert、锁定、优化器/索引/注释提示、命名参数以及子查询。

  • 复合主键、索引、约束:对于复合主键、索引和约束也有很好的支持。

  • 自动迁移(Auto Migrations): 支持自动数据库迁移。

  • 日志: 支持日志记录功能。

  • 插件API:提供可扩展、灵活的插件API, 如数据库解析器(支持多数据库、读写分离)/ Prometheus监控。

  • 测试完备:每一个功能都伴随着对应的测试用例。

GORM的基本使用 

安装

go get -u gorm.io/gorm

模型定义

在 GORM 中,模型通常由 Go 结构体表示,每一个模型对应数据库中的一个表。

type User struct {
	gorm.Model
	Name   string
	Age    uint
	Active bool
}

在上面的代码中,gorm.Model 是一个包含了 ID, CreatedAt, UpdatedAt, DeletedAt 字段的基础模型。我们在此基础上添加了自定义字段(Name, Age, Active)。

数据库连接和配置

假设您已经有一个运行中的关系型数据库(比如 PostgreSQL、MySQL 等),您可以使用以下方式连接到数据库:

package main

import (
	"gorm.io/driver/mysql" // 修改为相应的数据库驱动
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Name   string
	Age    uint
	Active bool
}

func main() {
	dsn := "username:password@protocol(address)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	// 接下来可以使用 `db` 句柄进行数据库操作
	// 比如迁移模式
	db.AutoMigrate(&User{})
}

连接数据库是使用数据库之前必须要做的步骤,上述代码展示了如何使用GORM连接到MySQL数据库。

CRUD 操作

// 创建
user := User{Name: "Alice", Age: 20}
db.Create(&user)

// 查询
var users []User
db.Find(&users)

// 更新
db.Model(&user).Update("age", 21)

// 删除
db.Delete(&user)
创建(C)

对数据库进行操作的第一步通常是向数据库中添加新记录。GORM使这个过程变得非常简单,示例如下:

上面的代码创建了一条新的用户记录。 

package main

import (
	"gorm.io/driver/mysql" // 修改为相应的数据库驱动
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Name   string
	Age    uint
	Active bool
}

func main() {
	dsn := "username:password@protocol(address)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	// 接下来可以使用 `db` 句柄进行数据库操作
	// 比如迁移模式
	db.AutoMigrate(&User{})

	user := User{Name: "Jinzhu", Age: 18, Active: true}
	result := db.Create(&user) // 通过数据模型创建记录

	// 检查错误
	if result.Error != nil {
		panic(result.Error)
	}
}
查询(R)

读取或查询数据库中现有数据是 ORM 最常用的功能之一。GORM 提供了灵活的查询方法。以下是查询单个记录的示例:

上述代码从数据库中查询ID为1的用户记录,并打印出用户名。 

package main

import (
	"fmt"
	"gorm.io/driver/mysql" // 修改为相应的数据库驱动
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Name   string
	Age    uint
	Active bool
}

func main() {
	dsn := "username:password@protocol(address)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	// 接下来可以使用 `db` 句柄进行数据库操作
	// 比如迁移模式
	db.AutoMigrate(&User{})

	var user User
	result := db.First(&user, 1) // 查询ID为1的用户

	// 检查错误
	if result.Error != nil {
		panic(result.Error)
	}

	fmt.Println(user.Name)
}
更新(U)

更新现有记录是另一个常见的数据库操作。GORM 提供了多种更新方法,以下是如何更新一条记录的示例:

此代码将用户名更新为"Jin" 

package main

import (
	"gorm.io/driver/mysql" // 修改为相应的数据库驱动
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Name   string
	Age    uint
	Active bool
}

func main() {
	dsn := "username:password@protocol(address)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	// 接下来可以使用 `db` 句柄进行数据库操作
	// 比如迁移模式
	db.AutoMigrate(&User{})

	var user User
	db.Model(&user).Update("Name", "Jin")
}
删除(D)

当需要从数据库中移除记录时,可以使用GORM的删除功能。在GORM中,删除可以是软删除(只更新DeletedAt字段,数据实际还在)或硬删除(实际从数据库中移除数据)。以下是删除一个用户的示例:

 上面的代码将从数据库中删除ID为1的用户。

package main

import (
	"gorm.io/driver/mysql" // 修改为相应的数据库驱动
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Name   string
	Age    uint
	Active bool
}

func main() {
	dsn := "username:password@protocol(address)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	// 接下来可以使用 `db` 句柄进行数据库操作
	// 比如迁移模式
	db.AutoMigrate(&User{})

	var user User
	db.Delete(&user, 1) // 删除 ID 为1的用户
}
关联处理

GORM提供了强大的关联处理能力,它支持一对一、一对多、多对多等关系。下面是一个一对多关系的例子,其中User有多个CreditCard

package main

import (
	"gorm.io/driver/mysql" // 修改为相应的数据库驱动
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	CreditCards []CreditCard
}

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

func main() {
	dsn := "username:password@protocol(address)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	// 接下来可以使用 `db` 句柄进行数据库操作
	// 比如迁移模式
	db.AutoMigrate(&User{}, &CreditCard{})

	var user User
	db.Preload("CreditCards").Find(&user)
}

上述代码中,Preload 函数预加载了用户的所有信用卡记录。这就是使用GORM处理关联的一个例子。

type User struct {
	gorm.Model
	Name   string
	Age    uint
	Active bool
}

type Profile struct {
    gorm.Model
    User User
}

// 一对多(Has Many)
type Post struct {
    gorm.Model
    Content string
    User    User
}

// 设置和创建关联
user := User{Name: "Alice"}
db.Create(&user)
db.Model(&user).Association("Posts").Create(&Post{Content: "First post"})

 以上代码创建User同时创建Post并关联User

GORM的高级使用

除了基本的CRUD操作,GORM还提供了高级功能,包括但不限于事务、Hooks、SQL构建器、日志记录等。这些功能可以帮助处理更复杂的场景,并使数据处理更加灵活和可控。

事务处理

tx := db.Begin()
defer func() {
    if r := recover(); r != nil {
        tx.Rollback()
    }
}()

// 执行一些操作...
tx.Commit()

在复杂的操作中,您可能需要按照事务来执行一系列数据库操作,确保数据的一致性和完整性。GORM 使得处理事务变得简单。以下是一个使用事务的例子:

package main

import (
	"gorm.io/driver/mysql" // 修改为相应的数据库驱动
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	CreditCards []CreditCard
}

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

func main() {
	dsn := "username:password@protocol(address)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	// 接下来可以使用 `db` 句柄进行数据库操作
	// 比如迁移模式
	db.AutoMigrate(&User{}, &CreditCard{})

	var user User
	var creditCard CreditCard
	// 开启一个事务
	tx := db.Begin()

	// 在事务中进行一系列操作
	tx.Create(&user)
	tx.Create(&creditCard)

	// 如果操作成功,则提交事务
	tx.Commit()

	// 如果中间产生了错误,您可以回滚这个事务
	tx.Rollback()
}

事务功能是确保数据安全性非常重要的一个功能。

钩子(Hooks)

GORM 允许您定义模型的钩子,例如在保存记录之前后自动执行特定功能。以下是定义BeforeSaveAfterCreate钩子的示例:

package main

import (
	"fmt"
	"gorm.io/driver/mysql" // 修改为相应的数据库驱动
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Name   string
	Age    uint
	Active bool
}

func (u *User) BeforeSave(tx *gorm.DB) (err error) {
	fmt.Println("每次Save操作之前,将打印相应的消息。")
	return
}

func (u *User) AfterCreate(tx *gorm.DB) (err error) {
	fmt.Println("每次Create操作之后,将打印相应的消息。")
	return
}
func main() {
	dsn := "username:password@protocol(address)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	// 接下来可以使用 `db` 句柄进行数据库操作
	// 比如迁移模式
	db.AutoMigrate(&User{})

	var user User
	db.Create(&user)
	db.Save(&user)
}

在以上代码中,每次Save操作之前和Create操作之后,将打印相应的消息。

type User struct {
    gorm.Model
    Name string
}

// 钩子示例:在创建之前清空名字字段
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
    u.Name = strings.ToUpper(u.Name)
    return
}

// 使用
user := User{Name: "alice"}
db.Create(&user) // 用户名将被转换为 ALICE

SQL构建器和日志记录

GORM 的SQL构建器非常强大,提供了灵活的查询方式。同时,GORM 的日志记录功能使得调试和检查变得简单:

db.Where("name = ?", "jinzhu").First(&user)

上述代码展示了如何构造一个普通的查询,并且 GORM 会记录此次查询的日志输出。 

调整日志级别

GORM 允许你设置不同的日志级别,以控制日志输出的详细程度。日志级别包括:

  • logger.Silent:不输出任何日志信息。
  • logger.Error:只输出错误信息。
  • logger.Warn:输出错误和警告信息。
  • logger.Info:输出错误、警告和一般信息,包括慢查询。
  • logger.Debug:输出所有 SQL 语句和执行时间。 
慢查询日志 

SlowThreshold 配置项允许你设置慢查询的阈值。在这个阈值以上的查询将被视为慢查询,并在日志中特别标记出来。 

彩色日志

Colorful 配置项允许你开启彩色日志输出,这可以使得日志输出更加易于阅读。

package main

import (
	"fmt"
	"gorm.io/driver/mysql" // 修改为相应的数据库驱动
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"log"
	"os"
	"time"
)

type User struct {
	gorm.Model
	Name   string
	Age    uint
	Active bool
}

func (u *User) BeforeSave(tx *gorm.DB) (err error) {
	fmt.Println("每次Save操作之前,将打印相应的消息。")
	return
}

func (u *User) AfterCreate(tx *gorm.DB) (err error) {
	fmt.Println("每次Create操作之后,将打印相应的消息。")
	return
}
func main() {
	dsn := "username:password@protocol(address)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	_, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: logger.New(
			log.New(os.Stdout, "\r\n", log.LstdFlags), // 日志输出
			logger.Config{
				SlowThreshold: 100 * time.Millisecond, // 慢查询阈值,任何执行时间超过 100 毫秒的查询都会被记录为慢查询
				LogLevel:      logger.Info,            // 日志级别
				Colorful:      true,                   // 彩色日志
			},
		),
	})
	if err != nil {
		panic("failed to connect database")
	}
}

慢查询日志(Slow Query Log)是数据库管理系统中的一种日志记录功能,用于记录执行时间超过预设阈值的 SQL 查询。这个特性在 MySQL、PostgreSQL 等数据库中都有支持,并且 GORM 也提供了相应的配置来帮助开发者捕捉和记录这些慢查询。

理解慢查询日志的几个关键点:
  1. 阈值(Threshold)

    • 阈值是指查询执行时间的上限,超过这个时间的查询将被认为是“慢查询”。
    • 在 GORM 中,可以通过 logger.Config 的 SlowThreshold 字段设置这个阈值。
  2. 日志记录

    • 慢查询日志记录了慢查询的详细信息,包括查询的 SQL 语句、执行时间、执行时的时间戳等。
    • 这些信息对于分析性能瓶颈和优化数据库查询非常有用。
  3. 性能分析

    • 通过分析慢查询日志,开发者可以识别出影响数据库性能的查询语句。
    • 可以进一步对这些查询进行优化,比如通过添加索引、改写查询逻辑或调整数据库结构。
  4. 实时监控

    • 在一些情况下,慢查询日志可以配置为实时输出,帮助开发者及时发现并处理性能问题。
  5. 资源消耗

    • 记录慢查询日志会消耗一定的系统资源,因为它需要额外的 I/O 操作来记录日志信息。
    • 因此,通常在开发环境或性能测试时开启慢查询日志,而在生产环境中根据需要谨慎使用。
  6. 配置和使用

    • 在 GORM 中,慢查询日志的记录可以通过配置 logger.Config 来开启。
    • 开启慢查询日志后,GORM 会在日志输出中标记出执行时间超过 SlowThreshold 的查询。

预加载

预加载可以减少数据库查询次数。

db.Preload("Profile").Find(&users)
var users []User
db.Preload("Profile").Find(&users, "age > ?", 18) // 预加载 Profile 关联并查询年龄大于 18 的用户

 上下文支持 

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

// 使用上下文执行查询
db.WithContext(ctx).Find(&users)

 批量操作

users := []User{{Name: "Alice"}, {Name: "Bob"}}
db.CreateInBatches(users, 10) // 批量创建用户

SQL 构建器

db.Table("users").Select("name, age").Where("age >= ?", 20).Scan(&users)

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【漏洞复现】英飞达医学影像存档与通信系统 WebJobUpload 任意文件上传漏洞

0x01 产品简介 英飞达医学影像存档与通信系统 Picture Archiving and Communicaton System,它是应用在医院影像科室的系统,主要的任务就是把日常产生的各种医学影像(包括核磁,CT,超声,各种X光机,各种红外仪…

如何编辑 PDF 中的文本?4个有效的编辑PDF方法

PDF 文件可以轻松打开和查看,但修改要复杂得多 - 尤其是在 PDF 中的文本编辑方面。 知道如何离线编辑 PDF 中的文本对于任何需要快速更改而无需在线加载文档或担心安全问题的人来说都非常有益。它使用户能够更好地控制他们的文档,并有更广泛的字体和图形…

详解 Scala 的集合类型

一、集合简介 1. 类型 序列 Seq:类似于 Java 中的 List 接口集 Set:类似于 Java 中的 Set 接口映射 Map:类似于 Java 中的 Map 接口所有的集合都扩展自 Iterable 特质 2. 不可变集合 位于 scala.collection.immutable 包,指该集…

【Linux】fork和exec中的信号继承探索

fork和exec中的信号继承探索 一、结论二、代码验证2.1 代码编写2.2 代码执行 三、linux源码验证四、APUE中的验证五、其他 一、结论 fork时子进程会继承父进程的信号处理方式,包括父进程设置信号为SIG_DFL或SIG_IGN或捕获后设置自定义处理函数。exce时子进程会继承…

有些错误,常犯常新、常新常犯:记录一个使用element-plus的tooltip组件的错误

使用element-plus的tooltip组件&#xff0c;最开始的写法是这样的&#xff1a; <el-tooltipclass"box-item"effect"dark"content"tooltip content" ><el-button v-if"isDisabled" :underline"false" type"pr…

一文扫尽Nas常用Docker软件

NAS&#xff08;Network Attached Storage&#xff0c;网络附加存储&#xff09;设备上的Docker软件选择取决于您的具体需求和用途。以下是一些NAS上常用的Docker软件推荐&#xff1a; Docker管理工具&#xff1a; Watchtower&#xff1a;它可以自动更新Docker容器中的镜像&…

从零开始学Vue3--环境搭建

1.搭建环境 下载nodejs nodejs下载地址 更新npm npm install -g npm 设置npm源&#xff0c;加快下载速度 npm config set registry https://registry.npmmirror.com 使用脚手架创建项目 npm create vuelatest 根据你的需要选择对应选项 进入新建的项目下载依赖 npm in…

kaggle竞赛实战3

接前文&#xff0c;本文主要做以下几件事&#xff1a; 1、把前面处理完的几个表拼成一个大表 2、做特征衍生&#xff08;把离散特征和连续特征两两组合得出&#xff09; # In[89]: #开始拼接表 transaction pd.concat([new_transaction, history_transaction], axis0, ignor…

STM32 USART的字符编码(发送器的实现逻辑)

目录 概述 1 字符编码 1.1 USART 字符说明 1.2 字长编程 2 发送器 2.1 字符发送 2.2 可配置的停止位 2.3 配置停止位方法 3 单字节通信 4 中断字符 5 空闲字符 概述 本文主要讲述STM32 USART的发送端功能实现的原理&#xff0c;包括字节编码长度&#xff0c;发送器…

Cyber Weekly #8

赛博新闻 1、微软召开年度发布会Microsoft Build 2024 本周&#xff08;5.22&#xff09;微软召开了年度发布会&#xff0c;Microsoft Build 2024&#xff0c;发布了包括大杀器 Copilot Studio 在内的 50 项更新。主要包括&#xff1a; 硬件层面&#xff1a;与英伟达 & A…

网络空间安全数学基础·群

重点&#xff1a; 1. 群及子群的定义及相关结论 2. 群的判断,子群的判断 3. 群的阶,元素的阶,它们的相互关系 4. 同态,同构,核子群 2.1群的定义 定义&#xff1a;设G是一非空集合。如果在G上定义了一个代数运算&#xff0c;称为乘法&#xff0c;记为ab&#xff0c;而且这个运…

【STL】C++ vector基本使用

目录 一 vector常见构造 1 空容器构造函数&#xff08;默认构造函数&#xff09; 2 Fill 构造函数 3 Range 构造函数 4 拷贝构造函数 5 C11构造 二 vector迭代器 1 begin && end 2 rbegin && rend 3 补充排序 三 vector 容量操作 1 size 2 resize …

进程信号(1)

目录 一、信号 1.1、生活中的信号 1.2、Linux中的信号 二、信号处理常见方式 三、信号的产生 3.1、简单理解信号的保存和发送 3.2、键盘产生信号 3.3、核心转储 3.4、系统调用接口产生信号 3.4.1、kill 3.4.2、raise 3.4.3、abort 3.5、软件条件产生信号 3.6、硬…

【操作系统】发展与分类(手工操作、批处理、分时操作、实时操作)

2.操作系统发展与分类 思维导图 手工操作阶段&#xff08;此阶段无操作系统&#xff09; 需要人工干预 缺点&#xff1a; 1.用户独占全机&#xff0c;资源利用率低&#xff1b; 2.CPU等待手工操作&#xff0c;CPU利用不充分。 批处理阶段&#xff08;操作系统开始出现&#x…

firewalld 防火墙

firewalld概述 Linux系统防火墙从CentOS7开始的默认防火墙工作在网络层&#xff0c;属于包过滤防火墙 Firewalld和iptables的关系 netfilter 位于Linux内核中的包过滤功能体系称为Linux防火墙的“内核态” firewalld Centos默认的管理防火墙规则的工具称为防火墙的“用…

【C++】右值引用 移动语义

目录 前言一、右值引用与移动语义1.1 左值引用和右值引用1.2 右值引用使用场景和意义1.3 右值引用引用左值及其一些更深入的使用场景分析1.3.1 完美转发 二、新的类功能三、可变参数模板 前言 本篇文章我们继续来聊聊C11新增的一些语法——右值引用&#xff0c;我们在之前就已…

sqli-lib4-6关教程

SERIES-4 输入?id1 输入?id2-1&#xff0c;说明该数据类型为字符型 输入?id1’ 输入?id1"&#xff0c;说明闭合符号为" 输入?id1")– 输入?id1") order by 3– 输入?id1") order by 4– 数据共三列&#xff0c;输入?id-1") union selec…

InfLLM的笔记

文件中提供的代码是一个Python函数chat_loop&#xff0c;它是聊天系统的核心循环。以下是对这段代码逻辑的梳理&#xff1a; 函数定义与参数 chat_loop函数接收多个参数&#xff0c;用于配置聊天模型和聊天环境。参数包括模型路径、设备类型、GPU数量、最大GPU内存、数据类型…

K8s的kubectl的基本操作

K8s的kubectl的基本操作 K8s基本信息的查看 查看版本信息 kubectl versio查看资源对象简写 kubectl api-resources查看集群信息 kubectl cluster-info配置kubectl自动补全 source <(kubectl completion bash)查看master节点状态 kubectl get cs查看命名空间 kubectl…

Elastic Cloud 将 Elasticsearch 向量数据库优化配置文件添加到 Microsoft Azure

作者&#xff1a;来自 Elastic Serena Chou, Jeff Vestal, Yuvraj Gupta 今天&#xff0c;我们很高兴地宣布&#xff0c;我们的 Elastic Cloud Vector Search 优化硬件配置文件现已可供 Elastic Cloud on Microsoft Azure 用户使用。 此硬件配置文件针对使用 Elasticsearch 作…