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

86 阅读7分钟

引言

GORM 是 Go 语言中一款优秀的 ORM(Object-Relational Mapping)库,它可以让我们用 Go 对象的方式来操作数据库,而不需要直接编写 SQL 语句。GORM 支持多种数据库,如 MySQL、PostgreSQL、SQLite、SQL Server 等,提供了丰富的功能和灵活的配置,让我们可以轻松地实现数据库的增删改查操作。

本文将介绍 GORM 的基本使用方法,包括如何安装和配置 GORM,如何定义和迁移数据模型,以及如何进行 CRUD(Create, Read, Update, Delete)操作。本文的示例代码使用 MySQL 数据库,但也适用于其他数据库。

安装和配置 GORM

要使用 GORM,首先需要安装 GORM 库和相应的数据库驱动。我们可以使用 go get 命令来安装:

// 安装 GORM 库
go get -u gorm.io/gorm

// 安装 MySQL 驱动
go get -u gorm.io/driver/mysql

安装完成后,我们就可以在代码中导入 GORM 库和驱动:

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

定义和迁移数据模型

在 GORM 中,我们可以使用结构体来定义数据模型,每个结构体对应一张数据表,每个字段对应一列数据。GORM 提供了一些标签(Tag)来控制结构体和数据表之间的映射关系,例如:

// 定义一个 User 结构体,对应 users 表
type User struct {
  ID        uint   `gorm:"primaryKey"` // 主键
  Name      string `gorm:"size:64"`    // 姓名,长度为 64
  Age       int    `gorm:"default:18"` // 年龄,默认为 18
  Email     string `gorm:"unique"`     // 邮箱,唯一约束
  CreatedAt time.Time                  // 创建时间
  UpdatedAt time.Time                  // 更新时间
}

如果没有指定标签,GORM 会根据一些默认规则来映射结构体和数据表。例如:

  • 结构体名对应表名的单数形式(如果表名是复数形式,需要设置 db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true}}) 来禁用复数表名)
  • 字段名对应列名的蛇形命名法(Snake Case),即大写字母转为小写并以下划线分隔
  • 如果字段类型是 time.Time 或 *time.Time,则会自动添加 NOT NULL DEFAULT CURRENT_TIMESTAMP 和 ON UPDATE CURRENT_TIMESTAMP 约束

有了结构体定义后,我们就可以使用 GORM 的 AutoMigrate 方法来自动创建或更新数据表了。AutoMigrate 方法会根据结构体的定义来创建或修改数据表,并保留原有的数据。如果数据表已经存在,AutoMigrate 方法会添加缺少的列或索引,但不会删除或修改已有的列或索引。例如:

// 根据 User 结构体自动创建或更新 users 表
err := db.AutoMigrate(&User{})
if err != nil {
  panic("failed to migrate database")
}

如果我们想要删除数据表,我们可以使用 Migrator 方法来获取一个迁移器对象,然后调用 DropTable 方法来删除指定的表。例如:

// 删除 users 表
err := db.Migrator().DropTable(&User{})
if err != nil {
  panic("failed to drop table")
}

进行 CRUD 操作

有了数据库连接和数据模型后,我们就可以使用 GORM 来进行 CRUD(Create, Read, Update, Delete)操作了。GORM 提供了一系列的方法来实现这些操作,我们可以通过链式调用的方式来组合不同的方法,以实现复杂的查询逻辑。下面我们分别介绍这些操作的基本用法。

Create

要向数据库中插入一条记录,我们可以使用 Create 方法,该方法接受一个结构体或切片作为参数,返回一个结果对象,该对象包含了插入操作的相关信息,例如影响的行数、错误信息等。例如:

// 创建一个 User 对象
user := User{Name: "Alice", Age: 20, Email: "alice@example.com"}

// 向 users 表中插入一条记录
result := db.Create(&user)

// 获取插入操作的结果
fmt.Println(result.RowsAffected) // 影响的行数
fmt.Println(result.Error)        // 错误信息
fmt.Println(user.ID)             // 自增主键的值

如果要向数据库中插入多条记录,我们可以使用切片作为参数,例如:

// 创建一个 User 切片
users := []User{
  {Name: "Bob", Age: 21, Email: "bob@example.com"},
  {Name: "Carol", Age: 22, Email: "carol@example.com"},
}

// 向 users 表中插入多条记录
result := db.Create(&users)

// 获取插入操作的结果
fmt.Println(result.RowsAffected) // 影响的行数
fmt.Println(result.Error)        // 错误信息
fmt.Println(users[0].ID)         // 第一条记录的主键值
fmt.Println(users[1].ID)         // 第二条记录的主键值

Read

要从数据库中查询一条或多条记录,我们可以使用 First、Take、Last、Find 等方法,这些方法都接受一个结构体或切片作为参数,并将查询结果赋值给该参数。如果查询成功,这些方法会返回一个结果对象,该对象包含了查询操作的相关信息,例如影响的行数、错误信息等。如果查询失败,这些方法会返回一个 ErrRecordNotFound 错误。例如:

// 查询 users 表中第一条记录,并赋值给 user 对象
var user User
result := db.First(&user)

// 获取查询操作的结果
fmt.Println(result.RowsAffected) // 影响的行数
fmt.Println(result.Error)        // 错误信息
fmt.Println(user)                // user 对象的值

// 查询 users 表中所有记录,并赋值给 users 切片
var users []User
result = db.Find(&users)

// 获取查询操作的结果
fmt.Println(result.RowsAffected) // 影响的行数
fmt.Println(result.Error)        // 错误信息
fmt.Println(users)               // users 切片的值

如果要对查询结果进行筛选或排序,我们可以使用 Where、Order、Limit 等方法来指定查询条件或选项。这些方法都会返回一个 *gorm.DB 类型的对象,该对象包含了查询语句的相关信息,我们可以继续调用其他方法来执行查询操作。例如:

// 查询 users 表中年龄大于 18 的记录,并按照年龄升序排序,只取前两条记录
var users []User
result := db.Where("age > ?", 18).Order("age asc").Limit(2).Find(&users)

// 获取查询操作的结果
fmt.Println(result.RowsAffected) // 影响的行数
fmt.Println(result.Error)        // 错误信息
fmt.Println(users)               // users 切片的值

这些方法的参数可以是字符串、结构体、切片、字典或函数,具体的用法可以参考 GORM 的文档。我们也可以使用 Raw 方法来直接编写 SQL 语句来执行查询操作,例如:

// 使用 SQL 语句查询 users 表中年龄最大的记录,并赋值给 user 对象
var user User
result := db.Raw("SELECT * FROM users ORDER BY age DESC LIMIT 1").Scan(&user)

// 获取查询操作的结果
fmt.Println(result.RowsAffected) // 影响的行数
fmt.Println(result.Error)        // 错误信息
fmt.Println(user)                // user 对象的值

Update

要更新数据库中的一条或多条记录,我们可以使用 Update、Updates、Save 等方法,这些方法都接受一个结构体或字典作为参数,并返回一个结果对象,该对象包含了更新操作的相关信息,例如影响的行数、错误信息等。例如:

// 更新 users 表中第一条记录的姓名为 "Alice"
result := db.Model(&User{}).Where("id = ?", 1).Update("name", "Alice")

fmt.Println(result.RowsAffected) // 影响的行数
fmt.Println(result.Error)        // 错误信息

result = db.Model(&User{}).Update("age", gorm.Expr("age + ?", 1))

// 获取更新操作的结果
fmt.Println(result.RowsAffected) // 影响的行数
fmt.Println(result.Error)        // 错误信息

// 使用 user 对象更新 users 表中对应记录的所有字段(除了主键和创建时间)
user := User{ID: 1, Name: "Alice", Age: 20, Email: "alice@example.com"}
result = db.Save(&user)

fmt.Println(result.RowsAffected) // 影响的行数
fmt.Println(result.Error)        // 错误信息

如果要更新多个字段,我们可以使用字典作为参数,例如:

// 更新 users 表中第一条记录的姓名和年龄为 "Alice" 和 20
result := db.Model(&User{}).Where("id = ?", 1).Updates(map[string]interface{}{"name": "Alice", "age": 20})

// 获取更新操作的结果
fmt.Println(result.RowsAffected) // 影响的行数
fmt.Println(result.Error)        // 错误信息

Delete

要删除数据库中的一条或多条记录,我们可以使用 Delete 方法,该方法接受一个结构体或切片作为参数,并返回一个结果对象,该对象包含了删除操作的相关信息,例如影响的行数、错误信息等。例如:

// 删除 users 表中第一条记录
result := db.Delete(&User{}, 1)

// 获取删除操作的结果
fmt.Println(result.RowsAffected) 
fmt.Println(result.Error)        

// 删除 users 表中所有记录
result = db.Delete(&User{})

// 获取删除操作的结果
fmt.Println(result.RowsAffected) 
fmt.Println(result.Error)        

// 删除 users 切片中对应的记录(根据主键)
users := []User{
  {ID: 2},
  {ID: 3},
}
result = db.Delete(&users)

// 获取删除操作的结果
fmt.Println(result.RowsAffected) 
fmt.Println(result.Error)      

如果要对删除结果进行筛选,我们可以使用 Where 方法来指定删除条件,例如:

// 删除 users 表中年龄小于 18 的记录
result := db.Where("age < ?", 18).Delete(&User{})

// 获取删除操作的结果
fmt.Println(result.RowsAffected) // 影响的行数
fmt.Println(result.Error)        // 错误信息

总结

本文介绍了 GORM 的基本使用方法,包括如何安装和配置 GORM,如何定义和迁移数据模型,以及如何进行 CRUD 操作。GORM 是一款功能强大、易用的 ORM 库,它可以让我们用 Go 对象的方式来操作数据库,而不需要直接编写 SQL 语句。