06 - GORM入门教程 | 青训营

643 阅读12分钟

GORM(Go的ORM库)入门教程

GORM是一个用于Go语言的对象关系映射(ORM)库,它可以让我们用Go语言的结构体和方法来操作数据库中的表和记录,而不需要直接编写SQL语句。GORM支持多种数据库驱动,包括MySQL、PostgreSQL、SQLite、SQL Server等。GORM提供了丰富的功能和灵活的API,让我们可以轻松地实现数据库的增、删、改、查等操作。

在本教程中,我们将学习如何使用GORM连接数据库,并实现增、删、改、查四种操作。我们将以MySQL数据库为例,但你也可以根据自己的需要选择其他数据库驱动。我们假设你已经安装了Go语言环境和MySQL数据库,并且在MySQL中创建了一个名为gorm_test的数据库。

连接数据库

要使用GORM连接数据库,我们首先需要导入gorm.io/gormgorm.io/driver/mysql两个包,分别提供了GORM的核心功能和MySQL驱动。然后,我们可以使用gorm.Open()函数来打开一个数据库连接,它接受两个参数:一个是数据库驱动,一个是数据源名称(DSN)。DSN是一个字符串,用于指定数据库的地址、端口、用户名、密码、名称等信息。我们可以使用fmt.Sprintf()函数来动态生成DSN,例如:

// 导入包
import (
  "fmt"
  "gorm.io/gorm"
  "gorm.io/driver/mysql"
)

// 定义数据库相关常量
const (
  DB_HOST = "localhost"
  DB_PORT = 3306
  DB_USER = "root"
  DB_PASS = "123456"
  DB_NAME = "gorm_test"
)

// 生成DSN
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", DB_USER, DB_PASS, DB_HOST, DB_PORT, DB_NAME)

// 打开数据库连接
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
  panic("failed to connect database")
}

如果连接成功,gorm.Open()函数会返回一个*gorm.DB类型的值,它代表了一个数据库连接会话,我们可以通过它来执行后续的操作。如果连接失败,它会返回一个错误值,我们可以通过panic()函数来抛出异常并终止程序。

定义模型

在GORM中,我们可以使用Go语言的结构体来定义模型(Model),它代表了数据库中的一张表。每个结构体字段对应了表中的一列,我们可以通过标签(Tag)来指定字段的名称、类型、约束等属性。例如,我们可以定义一个名为User的模型,它对应了数据库中的users表:

// 定义User模型
type User struct {
  ID        uint   `gorm:"primaryKey"` // 主键
  Name      string `gorm:"size:64"`    // 姓名,最大长度64
  Email     string `gorm:"unique"`     // 邮箱,唯一约束
  Password  string `gorm:"not null"`   // 密码,非空约束
  CreatedAt time.Time                  // 创建时间
  UpdatedAt time.Time                  // 更新时间
}

在上面的代码中,我们使用了gorm:"..."标签来指定GORM相关的属性,例如:

  • primaryKey表示该字段是主键,GORM会自动为其生成自增的值。
  • size:64表示该字段的最大长度是64个字符。
  • unique表示该字段具有唯一性约束,不能有重复的值。
  • not null表示该字段具有非空约束,不能为NULL。

我们也可以使用其他标签来指定非GORM相关的属性,例如:

  • json:"..."表示该字段在JSON格式中的名称,用于序列化和反序列化。
  • validate:"..."表示该字段的验证规则,用于数据校验。

创建表

在定义了模型之后,我们可以使用GORM的AutoMigrate()方法来自动创建或更新数据库中的表。它接受一个或多个模型类型作为参数,然后根据模型的定义来生成对应的SQL语句,并执行它们。例如,我们可以使用以下代码来创建users表:

// 创建users表
err = db.AutoMigrate(&User{})
if err != nil {
  panic("failed to create table")
}

如果表不存在,AutoMigrate()方法会创建一个新的表;如果表已经存在,它会根据模型的变化来更新表结构,但不会删除已有的列和数据。如果创建或更新失败,它会返回一个错误值,我们可以通过panic()函数来抛出异常并终止程序。

增加记录

在创建了表之后,我们可以使用GORM的Create()方法来向表中插入一条或多条记录。它接受一个或多个模型实例作为参数,然后根据模型的定义来生成对应的SQL语句,并执行它们。例如,我们可以使用以下代码来向users表中插入一条记录:

// 创建一个User实例
user := User{
  Name:     "Alice",
  Email:    "alice@example.com",
  Password: "123456",
}

// 向users表中插入一条记录
result := db.Create(&user)
if result.Error != nil {
  panic("failed to insert record")
}

如果插入成功,Create()方法会返回一个*gorm.DB类型的值,它包含了执行结果的相关信息,例如:

  • RowsAffected表示影响的行数,即插入的记录数。
  • Error表示执行过程中发生的错误,如果没有错误,则为nil。
  • PrimaryValue表示插入记录的主键值,如果有多个主键,则为第一个主键值。

如果插入失败,Create()方法会返回一个错误值,我们可以通过panic()函数来抛出异常并终止程序。

查询记录

在向表中插入了记录之后,我们可以使用GORM的各种查询方法来从表中查询一条或多条记录。这些查询方法都是基于*gorm.DB类型的值进行链式调用的,每个方法都会返回一个新的*gorm.DB类型的值,它包含了查询条件和结果。我们可以通过以下几种方式来指定查询条件:

  • 使用Where()方法来指定过滤条件,它接受一个字符串、一个结构体、一个字典或一个切片作为参数,然后根据参数的类型和内容来生成对应的SQL语句,并执行它们。例如:
// 查询姓名为Alice的用户
db.Where("name = ?", "Alice").Find(&users)

// 查询邮箱以.com结尾的用户
db.Where("email LIKE ?", "%.com").Find(&users)

// 查询ID在1到10之间的用户
db.Where("id BETWEEN ? AND ?", 1, 10).Find(&users)

// 查询ID为1或2或3的用户
db.Where("id IN ?", []int{1, 2, 3}).Find(&users)

// 查询创建时间在2023年1月1日之后的用户
db.Where("created_at > ?", "2023-01-01").Find(&users)

// 使用结构体作为参数,只会查询非零值字段
db.Where(&User{Name: "Alice", Email: "alice@example.com"}).Find(&usersid + 1")} }).Find(&results)

在指定了查询条件之后,我们可以使用以下几种方法来获取查询结果:

  • 使用Find()方法来查询多条记录,它接受一个切片的指针作为参数,然后将查询结果追加到该切片中。例如:
// 定义一个User切片
var users []User

// 查询所有用户,并将结果追加到users切片中
db.Find(&users)

// 查询姓名为Alice的用户,并将结果追加到users切片中
db.Where("name = ?", "Alice").Find(&users)
  • 使用First()方法来查询第一条记录,它接受一个结构体的指针作为参数,然后将查询结果赋值给该结构体。如果没有找到匹配的记录,它会返回一个ErrRecordNotFound错误。例如:
// 定义一个User结构体
var user User

// 查询第一条用户,并将结果赋值给user结构体
result := db.First(&user)
if result.Error != nil {
  // 处理错误
}

// 查询ID为1的用户,并将结果赋值给user结构体
result = db.Where("id = ?", 1).First(&user)
if result.Error != nil {
  // 处理错误
}
  • 使用Last()方法来查询最后一条记录,它接受一个结构体的指针作为参数,然后将查询结果赋值给该结构体。如果没有找到匹配的记录,它会返回一个ErrRecordNotFound错误。例如:
// 定义一个User结构体
var user User

// 查询最后一条用户,并将结果赋值给user结构体
result := db.Last(&user)
if result.Error != nil {
  // 处理错误
}

// 查询ID为10的用户,并将结果赋值给user结构体
result = db.Where("id = ?", 10).Last(&user)
if result.Error != nil {
  // 处理错误
}
  • 使用Take()方法来查询一条记录,它接受一个结构体的指针作为参数,然后将查询结果赋值给该结构体。如果没有找到匹配的记录,它会返回一个ErrRecordNotFound错误。例如:
// 定义一个User结构体
var user User

// 查询一条用户,并将结果赋值给user结构体
result := db.Take(&user)
if result.Error != nil {
  // 处理错误
}

// 查询姓名为Bob的用户,并将结果赋值给user结构体
result = db.Where("name = ?", "Bob").Take(&user)
if result.Error != nil {
  // 处理错误
}
  • 使用Count()方法来统计记录数,它接受一个整数的指针作为参数,然后将查询结果赋值给该整数。例如:
// 定义一个整数变量
var count int64

// 统计所有用户的数量,并将结果赋值给count变量
db.Model(&User{}).Count(&count)

// 统计姓名为Alice的用户的数量,并将结果赋值给count变量
db.Model(&User{}).Where("name = ?", "Alice").Count(&count)
  • 使用Pluck()方法来提取单个字段的值,它接受一个字符串和一个切片的指针作为参数,然后将查询结果追加到该切片中。例如:
// 定义一个字符串切片
var names []string

// 提取所有用户的姓名,并将结果追加到names切片中
db.Model(&User{}).Pluck("name", &names)

// 提取ID大于5的用户的姓名,并将结果追加到names切片中
db.Model(&User{}).Where("id > ?", 5).Pluck("name", &names)
  • 使用Scan()方法来扫描记录到任意类型的变量,它接受任意类型的变量作为参数,然后根据字段名或者标签来匹配查询结果,并赋值给该变量。例如:
// 定义一个结构体,用于接收查询结果
type Result struct {
  Name  string `gorm:"column:name"` // 指定字段名
  Count int    `gorm:"column:count"`
}

// 定义一个Result切片
var results []Result

// 按照姓名分组,统计每个姓名的用户数量,并将结果扫描到results切片中
db.Model(&User{}).Select("name, count(*) as count").Group("name").Scan(&results)

修改记录

在从表中查询了记录之后,我们可以使用GORM的Update()方法来修改一条或多条记录。它接受一个字符串、一个结构体、一个字典或一个函数作为参数,然后根据参数的类型和内容来生成对应的SQL语句,并执行它们。例如:

// 查询ID为1的用户,并将其赋值给user变量
var user User
db.First(&user, 1)

// 修改用户的姓名为Bob,使用字符串作为参数,表示要修改的字段名
db.Model(&user).Update("name", "Bob")

// 修改用户的邮箱和密码,使用结构体作为参数,只会修改非零值字段
db.Model(&user).Update(User{Email: "bob@example.com", Password: "654321"})

// 修改用户的邮箱和密码,使用字典作为参数,可以指定字段名和值
db.Model(&user).Update(map[string]interface{}{"email": "bob@example.com", "password": "654321"})

// 修改用户的邮箱和密码,使用函数作为参数,可以用闭包来动态生成字段名和值
db.Model(&user).Update(func(db *gorm.DB) interface{} {
  return map[string]interface{}{"email": "bob@example.com", "password": "654321"}
})

如果要修改多条记录,我们可以先指定查询条件,然后调用Update()方法。例如:

// 修改所有用户的密码为123456,使用字符串作为参数,表示要修改的字段名
db.Model(&User{}).Update("password", "123456")

// 修改所有用户的密码为123456,使用结构体作为参数,只会修改非零值字段
db.Model(&User{}).Update(User{Password: "123456"})

// 修改所有用户的密码为123456,使用字典作为参数,可以指定字段名和值
db.Model(&User{}).Update(map[string]interface{}{"password": "123456"})

// 修改所有用户的密码为123456,使用函数作为参数,可以用闭包来动态生成字段名和值
db.Model(&User{}).Update(func(db *gorm.DB) interface{} {
  return map[string]interface{}{"password": "123456"}
})

如果要修改多个字段,我们可以使用Updates()方法来一次性修改。它接受一个结构体、一个字典或一个函数作为参数,然后根据参数的类型和内容来生成对应的SQL语句,并执行它们。例如,我们可以使用结构体作为参数来修改用户的姓名和密码,这只会修改非零值字段。

// 修改所有用户的姓名、邮箱和密码,使用结构体作为参数,只会修改非零值字段
db.Model(&User{}).Updates(User{Name: "Tom", Email: "tom@example.com", Password: "123456"})

// 修改所有用户的姓名、邮箱和密码,使用字典作为参数,可以指定字段名和值
db.Model(&User{}).Updates(map[string]interface{}{"name": "Tom", "email": "tom@example.com", "password": "123456"})

// 修改所有用户的姓名、邮箱和密码,使用函数作为参数,可以用闭包来动态生成字段名和值
db.Model(&User{}).Updates(func(db *gorm.DB) interface{} {
  return map[string]interface{}{"name": "Tom", "email": "tom@example.com", "password": "123456"}
})

删除记录

在从表中查询了记录之后,我们可以使用GORM的Delete()方法来删除一条或多条记录。它接受一个结构体、一个切片或一个函数作为参数,然后根据参数的类型和内容来生成对应的SQL语句,并执行它们。例如:

// 查询ID为1的用户,并将其赋值给user变量
var user User
db.First(&user, 1)

// 删除用户,使用结构体作为参数,表示要删除的记录
db.Delete(&user)

// 删除用户,使用切片作为参数,表示要删除的记录
db.Delete([]User{{ID: 1}, {ID: 2}})

// 删除用户,使用函数作为参数,表示要删除的记录
db.Delete(func(db *gorm.DB) interface{} {
  return []User{{ID: 1}, {ID: 2}}
})

如果要删除多条记录,我们可以先指定查询条件,然后调用Delete()方法。例如:

// 删除所有用户
db.Delete(&User{})

// 删除姓名为Alice的用户
db.Where("name = ?", "Alice").Delete(&User{})

// 删除ID大于5的用户
db.Where("id > ?", 5).Delete(&User{})

总结

在本教程中,我们学习了如何使用GORM(Go的ORM库)连接数据库,并实现增、删、改、查四种操作。我们了解了如何定义模型、创建表、增加记录、查询记录、修改记录和删除记录。我们还学习了如何使用各种方法来指定查询条件和结果,并使用标签来指定字段属性。通过GORM,我们可以用Go语言的结构体和方法来操作数据库中的表和记录,而不需要直接编写SQL语句。这样可以提高我们的开发效率和代码可读性。