方向三:实践记录以及工具使用选题(10)

156 阅读8分钟

使用 GORM(Go 的 ORM 库)连接数据库并实现增删改查操作

在 Go 中,GORM 是一个用于 Go 语言的 ORM(对象关系映射)库,它提供了一种简单且高效的方式来操作数据库。通过 GORM,你可以用 Go 语言中的结构体来表示数据库中的表,并通过这些结构体来进行增删改查(CRUD)操作,而无需编写大量的 SQL 查询语句。GORM 的设计目标是让开发者能够以更简洁和直观的方式与数据库交互。

GORM 的特点

  1. 简洁的 API
    GORM 提供了直观且简洁的 API,支持链式调用,便于开发者快速进行数据操作。例如,执行查询、插入、更新和删除等操作非常简便。

  2. 结构体与表映射
    在 GORM 中,数据库的表可以通过 Go 结构体进行映射,结构体的字段对应数据库表的列,支持自动迁移(Auto Migration)功能,GORM 会自动创建或更新数据库表。

  3. 支持多种数据库
    GORM 支持多种数据库系统,包括:

    • MySQL
    • PostgreSQL
    • SQLite
    • SQL Server
    • 以及其他兼容的数据库
  4. 强大的查询功能
    GORM 支持链式查询,可以进行复杂的 SQL 查询构建,同时也支持 JoinWhereGroupOrder 等 SQL 操作,能够满足大多数查询需求。

  5. 事务支持
    GORM 支持事务操作,可以保证多个数据库操作的原子性。

  6. 迁移功能
    GORM 提供了自动迁移(Auto Migration)功能,可以根据模型结构自动创建和更新数据库表结构,简化了数据库的管理。

  7. 支持关联关系
    GORM 支持一对多、多对多和一对一的关系映射,并且可以方便地进行关联查询。

GORM 的安装

要使用 GORM,首先需要将其安装到 Go 项目中。你可以通过 go get 命令来安装:

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql  # 如果使用 MySQL 数据库

GORM 基本使用

1. 定义模型

在 GORM 中,数据库表和 Go 语言的结构体直接对应。通过结构体标签,你可以控制字段的映射方式。例如:

type User struct {
    ID        uint   `gorm:"primaryKey"`
    Name      string `gorm:"size:100"`
    Age       int
    CreatedAt string `gorm:"autoCreateTime"`
}
  • ID 是主键。
  • Name 字段在数据库中最大为 100 个字符。
  • CreatedAt 字段会自动生成创建时间。
2. 连接数据库

要连接到数据库,使用 gorm.Open 方法。在连接 MySQL 数据库时,你需要提供一个数据库连接字符串:

dsn := "root:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
    panic("failed to connect to the database")
}
3. 数据库操作(增删改查)

创建(Insert)

user := User{Name: "John", Age: 25}
db.Create(&user)

查询(Read)

查询单个记录:

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

查询所有记录:

var users []User
db.Find(&users) // 查询所有用户

更新(Update)

var user User
db.First(&user, 1)
user.Age = 30
db.Save(&user) // 更新用户

删除(Delete)

var user User
db.First(&user, 1)
db.Delete(&user) // 删除用户
4. 自动迁移

GORM 提供了 AutoMigrate 方法,可以根据模型自动创建或更新数据库表结构:

db.AutoMigrate(&User{})
5. 事务支持

GORM 支持数据库事务,以下是事务的基本用法:

tx := db.Begin() // 开始事务

if err := tx.Create(&user).Error; err != nil {
    tx.Rollback() // 回滚事务
    return err
}

tx.Commit() // 提交事务
6. 关联查询

GORM 支持一对多、多对多、一对一的关系。在定义模型时,你可以使用 GORM 的关联标签来描述这些关系。

例如,一对多关系:

type Post struct {
    ID     uint
    Title  string
    UserID uint
    User   User
}

type User struct {
    ID    uint
    Name  string
    Posts []Post
}

然后可以进行关联查询:

var user User
db.Preload("Posts").First(&user, 1) // 加载用户和用户的所有帖子

GORM 的高级功能

除了基本的增删改查操作,GORM 还提供了许多高级功能,例如:

  • Hooks: 支持模型的钩子函数,如 BeforeSaveAfterCreate 等,可以在数据操作前后执行自定义逻辑。

  • 自定义 SQL 查询: GORM 支持原生 SQL 查询,你可以直接执行 SQL 语句,例如:

    db.Raw("SELECT * FROM users WHERE name = ?", "John").Scan(&users)
    
  • 分页查询: 你可以使用 Limit 和 Offset 来实现分页查询:

    var users []User
    db.Limit(10).Offset(10).Find(&users) // 查询第 2 页的数据
    

1. 准备工作

在开始之前,我们需要确保以下几个步骤已经完成:

  • 安装 Go 环境:确保你的计算机已经安装了 Go 环境。可以在 Go 官方网站 下载并安装。

  • 安装 GORM:可以通过 go get 命令安装 GORM 库。

  • 准备数据库:本文示例使用 MySQL 数据库,你需要确保数据库已经创建好,并且数据库的用户名、密码等信息正确。

2. 安装 GORM

首先,在你的 Go 项目中安装 GORM:

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

这两条命令会安装 GORM 和 MySQL 驱动。

3. 连接数据库

在 Go 中使用 GORM 连接 MySQL 数据库,我们需要提供数据库的连接字符串。典型的 MySQL 连接字符串格式为:

用户名:密码@tcp(数据库地址:端口)/数据库名?charset=utf8mb4&parseTime=True&loc=Local

例如,假设你的 MySQL 数据库用户名为 root,密码为 password,数据库名为 testdb,你可以使用以下代码来连接数据库。

代码示例:连接 MySQL 数据库

package main

import (
	"fmt"
	"log"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var db *gorm.DB

func main() {
	// 数据库连接字符串
	dsn := "root:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"

	// 连接到 MySQL 数据库
	var err error
	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatalf("failed to connect to database: %v", err)
	}

	// 确认数据库连接
	fmt.Println("Database connection established successfully!")
}

此段代码使用 gorm.Open 函数来连接 MySQL 数据库,并在连接失败时输出错误信息。如果连接成功,它将打印确认消息。

4. 定义模型

在 GORM 中,数据库中的表与 Go 结构体(Struct)直接对应。你需要定义一个结构体,并使用 GORM 的标签来映射表字段。

假设我们有一个 User 表,表结构如下:

FieldType
idint
namestring
ageint
created_atdatetime

我们可以使用 GORM 定义一个对应的结构体:

代码示例:定义模型

type User struct {
	ID        uint   `gorm:"primaryKey"`
	Name      string `gorm:"size:100"`
	Age       int
	CreatedAt string `gorm:"autoCreateTime"`
}

在这个结构体中:

  • ID 字段是主键,GORM 会自动识别并处理。

  • Name 和 Age 字段对应数据库中的 name 和 age 列。

  • CreatedAt 字段使用 autoCreateTime 标签来自动生成创建时间。

5. 实现增删改查操作

5.1 创建(Insert)

通过 Create 方法,我们可以向数据库中插入一条新的记录。

代码示例:插入数据

func createUser() {
	user := User{Name: "John Doe", Age: 30}
	result := db.Create(&user)
	if result.Error != nil {
		log.Fatalf("failed to create user: %v", result.Error)
	}
	fmt.Printf("User created with ID: %d\n", user.ID)
}

在这个例子中,我们创建了一个新的 User 实例,并通过 db.Create(&user) 将其插入到数据库中。插入成功后,我们打印新插入记录的 ID

5.2 查询(Read)

使用 FindFirst 方法可以从数据库中查询数据。Find 用于查询所有记录,First 用于查询符合条件的第一条记录。

代码示例:查询数据

func getUser() {
	var user User
	result := db.First(&user, 1) // 查询 ID = 1 的用户
	if result.Error != nil {
		log.Fatalf("failed to get user: %v", result.Error)
	}
	fmt.Printf("User found: %v\n", user)
}

func getAllUsers() {
	var users []User
	result := db.Find(&users) // 查询所有用户
	if result.Error != nil {
		log.Fatalf("failed to get users: %v", result.Error)
	}
	for _, user := range users {
		fmt.Printf("User: %v\n", user)
	}
}
  • First(&user, 1) 查询 ID 为 1 的用户。
  • Find(&users) 查询所有用户。

5.3 更新(Update)

通过 SaveUpdates 方法,我们可以更新已存在的记录。

代码示例:更新数据

func updateUser() {
	var user User
	result := db.First(&user, 1) // 查询 ID = 1 的用户
	if result.Error != nil {
		log.Fatalf("failed to find user: %v", result.Error)
	}

	// 更新用户的年龄
	user.Age = 35
	result = db.Save(&user)
	if result.Error != nil {
		log.Fatalf("failed to update user: %v", result.Error)
	}
	fmt.Printf("User updated: %v\n", user)
}

在这个例子中,我们首先查询到 ID 为 1 的用户,并修改了其 Age 字段,然后调用 Save 方法保存更新。

5.4 删除(Delete)

使用 Delete 方法可以删除数据库中的记录。

代码示例:删除数据

func deleteUser() {
	var user User
	result := db.First(&user, 1) // 查询 ID = 1 的用户
	if result.Error != nil {
		log.Fatalf("failed to find user: %v", result.Error)
	}

	// 删除该用户
	result = db.Delete(&user)
	if result.Error != nil {
		log.Fatalf("failed to delete user: %v", result.Error)
	}
	fmt.Println("User deleted successfully!")
}

在这个例子中,我们查询到 ID 为 1 的用户,然后通过 Delete 方法将其从数据库中删除。

6. 代码汇总

以下是完整的示例代码,包括数据库连接、模型定义和增删改查操作。

package main

import (
	"fmt"
	"log"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var db *gorm.DB

type User struct {
	ID        uint   `gorm:"primaryKey"`
	Name      string `gorm:"size:100"`
	Age       int
	CreatedAt string `gorm:"autoCreateTime"`
}

func main() {
	// 连接数据库
	dsn := "root:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
	var err error
	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatalf("failed to connect to database: %v", err)
	}

	// 自动迁移数据库(创建或更新表结构)
	db.AutoMigrate(&User{})

	// 操作示例
	createUser()
	getUser()
	getAllUsers()
	updateUser()
	deleteUser()
}

func createUser() {
	user := User{Name: "John Doe", Age: 30}
	result := db.Create(&user)
	if result.Error != nil {
		log.Fatalf("failed to create user: %v", result.Error)
	}
	fmt.Printf("User created with ID: %d\n", user.ID)
}

func getUser() {
	var user User
	result := db.First(&user, 1)
	if result.Error != nil {
		log.Fatalf("failed to get user: %v", result.Error)
	}
	fmt.Printf("User found: %v\n", user)
}

func getAllUsers() {
	var users []User
	result := db.Find(&users)
	if result.Error != nil {
		log.Fatalf("failed to get users: %v", result.Error)
	}
	for _, user := range users {
		fmt.Printf("User: %v\n", user)
	}
}

func updateUser() {
	var user User
	result := db.First(&user, 1) // 查询 ID = 1 的用户
	if result.Error != nil {
		log.Fatalf("failed to find user: %v", result.Error)
	}

	// 更新用户的年龄
	user.Age = 35
	result = db.Save(&user)
	if result.Error != nil {
		log.Fatalf("failed to update user: %v", result.Error)
	}
	fmt.Printf("User updated: %v\n", user)
}

func deleteUser() {
	var user User
	result := db.First(&user, 1) // 查询 ID = 1 的用户
	if result.Error != nil {
		log.Fatalf("failed to find user: %v", result.Error)
	}

	// 删除该用户
	result = db.Delete(&user)
	if result.Error != nil {
		log.Fatalf("failed to delete user: %v", result.Error)
	}
	fmt.Println("User deleted successfully!")
}