GO语言学习(17)Gorm的数据库操作

发布于:2025-04-05 ⋅ 阅读:(24) ⋅ 点赞:(0)

目录

🏆前言

1.Gorm的简介

2.GORM连接数据库 

2.1 配置DSN 

Mysql,TiDB,MariaDB

PostgreSQL

SQL Server

 SQLite

2.2 gorm.Open连接数据库

3.数据库连接池的配置  

4.使用GORM对数据库进行操作(重点) 

4.1 创建数据表

4.2 CRUD操作 

GORM利用原生SQL执行

🏆前言

        通过上一篇Go语言学习(16)Gin后端框架的学习,了解了基本的Go语言如何使用Gin进行前后端数据的传输,本文将进一步学习后端,了解如何使用Gorm与Mysql数据库进行数据交互,实现基本的增删改查功能。

1.Gorm的简介

        GORM 是 Go 语言中最流行的 ORM(对象关系映射)库之一,支持主流数据库(MySQL、PostgreSQL、SQLite、SQL Server 等),提供简洁的 API 和强大的功能。其具有以下基本特点:

  • 简洁易用:通过定义结构体来映射数据库表,简化数据操作。
  • 功能全面:支持CRUD、事务、预加载、关联关系、自动迁移等常见功能。
  • 扩展性强:内置钩子函数、插件机制以及对多种数据库(MySQL、PostgreSQL、SQLite、SQL Server等)的支持。
  • 性能优秀:经过大量优化,能够在高并发场景下保持稳定性能。 

        当然Go语言当中的ORM库不止Gorm一个,以下是一些其它Go当中的ORM库的比较,可根据自身需求,进行选择学习:

技术栈 性能 易用性 功能 适用场景
Gorm 优秀,适合高并发场景 非常友好,API简洁明了,文档齐全 功能全面,支持多种数据库,扩展性强 适合各种规模的项目,尤其是需要高性能和复杂数据库操作的场景
Xorm 性能优异,经过大量优化 API简洁,支持链式调用 支持基本的CRUD操作,查询构建器灵活 适合中小型项目,特别是对性能要求较高的场景
Beego ORM 性能中等 与Beego框架无缝集成,使用方便 功能全面,但非Beego项目中配置复杂 适合使用Beego框架的Web开发项目
Go-PG 针对PostgreSQL优化,性能良好 API设计符合PostgreSQL习惯,但学习成本较高 充分利用PostgreSQL特性,支持高级查询和事务管理 适用于PostgreSQL数据库的项目
upper.io/db 性能中等 模块化设计,易于扩展和定制,但配置复杂 支持多种数据库,模块化设计 适用于需要支持多种数据库的复杂项目

🍅 GORM的官方文档连接👉:GORM官方文档 

2.GORM连接数据库 

       首先我们需要使用以下命令,安装GORM与Mysql驱动引入到本地项目当中:

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

        使用GORM连接数据库,大体可以分为两个步骤:

  1. 🌸配置DSN(Data Source Name,数据源名称)
  2. 🌸使用gorm.Open连接数据库

2.1 配置DSN 

         DSN(Data Source Name) 是一种用于连接数据库的字符串,它包含了连接到数据库所需的所有信息,如数据库类型、主机地址、端口号、用户名、密码、数据库名称等。DSN通常用于数据库驱动程序中,以简化数据库连接的配置。不同的数据库连接中DSN的配置也不相同。

  • Mysql,TiDB,MariaDB

dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"

//填上参数后的例子
//username = root
//password = 123456
//host     = localhost
//port     = 3306
//Dbname   = gorm
root:123456@tcp(localhost:3306)/gorm?charset=utf8&parseTime=True&loc=Local

 🌹各参数含义:

  1. user:数据库用户名。
  2. password:数据库密码。
  3. 127.0.0.1:数据库服务器的IP地址。
  4. 3306:数据库服务监听的端口号。
  5. dbname:要连接的数据库名称。
  6. charset=utf8mb4:字符集设置。
  7. parseTime=True:启用时间解析。
  8. loc=Local:时区设置。

        由于TiDB,MariaDB与MySQL在功能上几乎完全兼容,因此可以按照MySQL的DSN格式来连接:,但需要注意TiDB的默认端口号为4000,而不是3306。

  • PostgreSQL

dsn := "host=localhost user=gorm dbname=gorm password=gorm port=5432 sslmode=disable"

🌹各参数含义:

  1. host:数据库服务器的主机名或IP地址。
  2. user:数据库用户名。
  3. dbname:要连接的数据库名称。
  4. password:数据库密码。
  5. port:数据库服务监听的端口号。
  6. sslmode:SSL模式,如disable、require等 
  • SQL Server

dsn := "sqlserver://username:password@localhost:1433?database=your_db"

 🌹各参数含义:

  1. username:数据库用户名。
  2. password:数据库密码。
  3. localhost:数据库服务器的主机名或IP地址。
  4. 1433:数据库服务监听的端
  5. your_db:要连接的数据库名称。
  •  SQLite

dsn := "gorm.db"

SQLite的DSN通常是一个文件路径,表示数据库文件的位置。

2.2 gorm.Open连接数据库

        配置好DSN之后,便可以使用open函数连接数据库,下面以Mysq连接为例子。

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

         使用mysql.open读取dsn进行数据库连接,&gorm.Config{} 用于配置 GORM 的全局行为(可选参数),例如:Logger: 自定义日志输出;SkipDefaultTransaction: 禁用默认事务;NamingStrategy: 表名和列名命名策略,此处使用空结构体 {} 表示使用默认配置。返回的db是一个 *gorm.DB 对象,代表数据库连接池,后续所有数据库操作都基于此对象。其余数据库连接也是同理。

此外GORM 允许通过 DriverName 选项自定义 MySQL 驱动,例如: 

import (
  _ "example.com/my_mysql_driver"
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)

db, err := gorm.Open(mysql.New(mysql.Config{
  DriverName: "my_mysql_driver",
  DSN: "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local", // data source name, 详情参考:https://github.com/go-sql-driver/mysql#dsn-data-source-name
}), &gorm.Config{})

3.数据库连接池的配置  

        数据库连接池就像餐厅里的一群“备用服务员”。这些服务员已经站在厨房门口,随时准备接单。当顾客来点菜时,直接从这群备用服务员中挑一个,用完后服务员会回到原位,继续等待下一位顾客。这样就避免了每次都要重新找服务员(建立连接)的麻烦,效率更高。然而连接池的设置并不能过大或者或小。

连接池设置过大的影响:

  1. 资源浪费:设置过大的连接池可能导致过多的数据库连接被创建,占用过多的系统资源(如内存和CPU),从而导致资源浪费。
  2. 性能下降:过多的连接可能导致数据库服务器负载过高,影响整体应用程序性能,甚至可能导致系统崩溃。
  3. 资源碎片化:过多的连接可能导致系统资源碎片化,降低系统的稳定性和响应速度。

连接池设置过小的影响:

  1. 连接不足:设置过小的连接池可能导致在高并发情况下请求无法及时获取数据库连接,从而影响并发性能和响应速度。
  2. 频繁创建和销毁连接:连接池过小会导致频繁地创建和销毁连接,增加了系统的开销。
  3. 请求等待:在高并发情况下,请求可能会等待空闲连接,导致延迟增加,用户体验下降

在GORM中,我们可以参照以下示例,对连接池进行有关配置。 

dsn := "root:123456@tcp(127.0.0.1:3306)/golang?charset=utf8mb4&parseTime=True&loc=Local"
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

sqlDB, err := db.DB()
if err != nil {
    panic("failed to get database connection")
}

// 设置最大打开的连接数
sqlDB.SetMaxOpenConns(10)
// 设置最大空闲连接数
sqlDB.SetMaxIdleConns(5)
// 设置连接的最大存活时间
sqlDB.SetConnMaxLifetime(time.Hour)

下面解释一下上述代码最后三行代码的配置分别代表什么,有什么作用。

// 设置最大打开的连接数
sqlDB.SetMaxOpenConns(10)

🌈通俗解释
想象你开了一家餐厅,最多只能同时雇佣10个服务员。无论有多少顾客来,你最多只能同时派10个服务员去服务。这个数字就是“最大打开的连接数”。

☀️技术解释
最大打开的连接数是指数据库连接池中同时可以打开的数据库连接的最大数量。如果设置为10,那么最多只能有10个连接同时处于“打开”状态,用于执行数据库操作。如果所有连接都被占用,新的请求需要等待,直到有连接被释放。

// 设置最大空闲连接数
sqlDB.SetMaxIdleConns(5)

🌈通俗解释:
继续用餐厅的例子,假设你有10个服务员,但并不是所有服务员都一直有活干。有些服务员可能暂时没事做(空闲)。你希望最多有5个服务员可以“待命”,随时准备接单。这个数字就是“最大空闲连接数”。
☀️技术解释:
最大空闲连接数是指连接池中允许存在的空闲连接的最大数量。空闲连接是指当前没有被使用的连接。如果空闲连接数超过这个值,多余的连接会被关闭,以节省资源。

// 设置连接的最大存活时间
sqlDB.SetConnMaxLifetime(time.Hour)

🌈通俗解释
假设你规定每个服务员最多只能连续工作1小时,之后必须休息或换人。这个时间限制就是“连接最大存活时间”。

☀️技术解释
连接最大存活时间是指一个数据库连接在连接池中可以存在的最长时间。如果一个连接已经存在了超过这个时间,它会被自动关闭,并从连接池中移除。这样可以确保连接不会因为长时间使用而出现性能问题或资源泄漏。

4.使用GORM对数据库进行操作(重点) 

        首先,如果对结构体以及标签(Tag)不熟悉的uu,还请移步到结构体标签与反射机制,GORM对数据库的基本操作与结构体类型数据非常相关。对结构体不熟悉的话,可能下面的代码会看不懂QAQ。

4.1 创建数据表

        假设我们现在需要构建一个 Students 表,那么我们得先创建一个Students的结构体类型数据,这一步我们称为 定义操作模型 。如下:

type Students struct {
	StudentID uint   `gorm:"primaryKey;autoIncrement"`
	Name      string `gorm:"size:50;not null"`
	Major     string `gorm:"size:100"`
	Gender    string `gorm:"type:enum('男', '女');not null"`
	Phone     string `gorm:"size:11;not null"`
}

        这里结构体使用 gorm 标签,以下是关于 gorm 标签常用的标签字段表:

标签 说明 示例
primaryKey 标记字段为主键 gorm:"primaryKey"
autoIncrement 主键自增(仅支持整数类型) gorm:"primaryKey;autoIncrement"
column 自定义数据库列名 gorm:"column:user_name"
type 指定数据库字段类型 gorm:"type:varchar(100)"
size 设置字段长度(如字符串类型) gorm:"size:255"
default 设置字段默认值 gorm:"default:'anonymous'"
not null 字段不可为空 gorm:"not null"
unique 唯一约束(值不可重复) gorm:"unique"
uniqueIndex 创建唯一索引(可指定索引名) gorm:"uniqueIndex:idx_email"
index 创建普通索引 gorm:"index"
check 自定义检查约束 gorm:"check:age > 18"
autoCreateTime 记录创建时间(time.Time 类型) gorm:"autoCreateTime"
autoUpdateTime 记录更新时间(time.Time 类型) gorm:"autoUpdateTime"
foreignKey 定义外键字段 gorm:"foreignKey:UserID"
references 定义关联的主键字段 gorm:"references:ID"
many2many 定义多对多关联的中间表 gorm:"many2many:user_groups;"
polymorphic 多态关联(用于多模型共享关联) gorm:"polymorphic:Owner"

         接着连接数据库之后,使用 db.AutoMigrate() 启用自动迁移模式,此时如果Students表不存在,则会依据上述定义的结构体,自动创建一个Students表,如果表已存在但是表的结构与结构体的结构不一样,则会自动更新数据表的结构与结构体保持一致。 

	// 连接到Mysql数据库
	dsn := "root:123456@tcp(127.0.0.1:3306)/golang?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	//自动迁移模式(如果表不存在,会自动创建)
	db.AutoMigrate(&Students{})

我们可以使用 Navicat 或者 sql语句 直接查看创建的数据表:

这里补充一下,GORM的结构体字段到数据库字段的转化是采取 Snake Case 风格转换:

  • 结构体的名称必须首字母大写 ,并和数据库表名称对应。
  • 结构体中的字段名称首字母必须大写,并和数据库表中的字段一一对应
  • Snake Case命名风格,就是各个单词之间用下划线(_)分隔,例如: CreateTime的Snake Case风格命名为create_time。
  • 在创建数据表过程中,默认情况表名是结构体名称的复数形式。如果我们的结构体名称定义成 User,表示这个模型默认操作的是 users 表。

4.2 CRUD操作 

下面以User表作为示例:

type User struct {
  ID    uint   `gorm:"primaryKey"` // 主键
  Name  string `gorm:"size:100"`    // 字符串长度限制
  Age   int
  Email string `gorm:"unique"`     // 唯一约束
}

1.新增数据:

// 单条插入
user := User{Name: "Alice", Age: 25, Email: "alice@example.com"}
result := db.Create(&user) // 插入数据
if result.Error != nil {
  panic("插入失败: " + result.Error.Error())
}
fmt.Println("插入成功,ID:", user.ID) // 自动填充主键

// 批量插入
users := []User{
  {Name: "Bob", Age: 30, Email: "bob@example.com"},
  {Name: "Charlie", Age: 28, Email: "charlie@example.com"},
}
db.Create(&users)

2.查询数据

// 查询单条(按主键)
var user User
db.First(&user, 1) // 查询 ID=1 的记录
fmt.Println(user.Name)

// 条件查询
db.Where("age > ?", 20).First(&user)   // 第一条符合条件的记录
db.Where("name LIKE ?", "%Ali%").Find(&user)

// 查询多条
var users []User
db.Where("age BETWEEN ? AND ?", 20, 30).Find(&users)

// 选择特定字段
db.Select("name", "age").Find(&users)

// 排序和分页
db.Order("age desc").Limit(10).Offset(0).Find(&users)

3.更新数据

// 更新单个字段
db.Model(&user).Update("age", 26) // 将 user 的 age 更新为 26

// 更新多个字段
db.Model(&user).Updates(User{Name: "Alice Smith", Age: 27})

// 条件更新(更新所有符合条件的记录)
db.Where("age < ?", 18).Updates(User{Name: "未成年"})

// 注意:Updates 默认忽略零值(如 Age:0),需用 Select 强制更新
db.Model(&user).Select("Age").Updates(User{Age: 0})

4.删除数据

// 删除单条记录(软删除,需模型包含 DeletedAt 字段)
db.Delete(&user) // 实际执行 UPDATE 设置 deleted_at 时间

// 硬删除(物理删除)
db.Unscoped().Delete(&user) // 直接从数据库删除

// 条件删除
db.Where("email LIKE ?", "%example.com").Delete(&User{})
  • 软删除:并不会真正从数据库中移除数据,而是通过添加一个标记字段(如is_deleted或deleted_at)来表示数据是否被“删除”。查询时,通常会过滤掉这些被标记为已删除的数据。
  • 硬删除:硬删除会直接从数据库中移除数据,这些数据一旦删除,就无法恢复(除非有备份)

GORM利用原生SQL执行

        以上的CRUD操作都是基于GORM的封装函数实现,如果习惯了写 sql 的uu,也可以使用 db.Exec()以及db.Raw()来执行sql语句,具体使用可参考下面的示例代码

完整实例:

package main

import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

type User struct {
    ID    uint
    Name  string
    Age   int
    Email string
}

func main() {
    dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        panic("数据库连接失败")
    }

    // 插入
    db.Exec("INSERT INTO users (name, age, email) VALUES (?, ?, ?)", "李四", 28, "lisi@example.com")

    // 查询
    var user User
    db.Raw("SELECT * FROM users WHERE name = ?", "李四").Scan(&user)
    fmt.Printf("查询结果: %+v\n", user)

    // 更新
    db.Exec("UPDATE users SET age = ? WHERE name = ?", 30, "李四")

    // 删除
    db.Exec("DELETE FROM users WHERE name = ?", "李四")
}

        至此17篇,我们终于学完了Go的后端开发的最为基础的内容。