Go GORM介绍

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 复制代码
go get -u gorm.io/gorm

模型定义

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

Go 复制代码
type User struct {
	gorm.Model
	Name   string
	Age    uint
	Active bool
}

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

数据库连接和配置

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

Go 复制代码
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 操作

Go 复制代码
// 创建
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使这个过程变得非常简单,示例如下:

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

Go 复制代码
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的用户记录,并打印出用户名。

Go 复制代码
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"

Go 复制代码
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的用户。

Go 复制代码
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

Go 复制代码
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处理关联的一个例子。

Go 复制代码
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构建器、日志记录等。这些功能可以帮助处理更复杂的场景,并使数据处理更加灵活和可控。

事务处理

Go 复制代码
tx := db.Begin()
defer func() {
    if r := recover(); r != nil {
        tx.Rollback()
    }
}()

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

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

Go 复制代码
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钩子的示例:

Go 复制代码
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操作之后,将打印相应的消息。

Go 复制代码
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 的日志记录功能使得调试和检查变得简单:

Go 复制代码
db.Where("name = ?", "jinzhu").First(&user)

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

调整日志级别

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

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

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

彩色日志

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

Go 复制代码
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.ConfigSlowThreshold 字段设置这个阈值。
  2. 日志记录

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

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

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

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

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

预加载

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

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

上下文支持

Go 复制代码
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

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

批量操作

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

SQL 构建器

Go 复制代码
db.Table("users").Select("name, age").Where("age >= ?", 20).Scan(&users)
相关推荐
Jason-河山1 小时前
利用Java爬虫获得店铺详情:技术解析
java·开发语言·爬虫
ᝰꫝꪉꪯꫀ3612 小时前
JavaWeb——Maven高级
java·后端·maven·springboot
就是有点傻2 小时前
C#中面试的常见问题005
开发语言·面试·c#·wpf
一舍予3 小时前
nuxt3项目搭建相关
开发语言·javascript·vue.js·nuxt
AI人H哥会Java4 小时前
【JAVA】Java基础—面向对象编程:常用API与数据结构—集合框架(List、Set、Map等)
java·开发语言
shepherd枸杞泡茶4 小时前
C# 数据结构之【队列】C#队列
开发语言·数据结构·c#
scoone4 小时前
C++中的原子操作:原子性、内存顺序、性能优化与原子变量赋值
开发语言·c++
轩情吖4 小时前
模拟实现Bash
linux·c语言·开发语言·c++·后端·bash·环境变量
旧故新长5 小时前
版本控制和idea简体中文教程
java·开发语言·intellij-idea
疯一样的码农5 小时前
使用 Maven 构建一个简单的 Java 项目
java·开发语言·maven