GORM 学习笔记 | 豆包MarsCode AI刷题

28 阅读6分钟

GORM 学习笔记

介绍

gorm是GoLang实现的,在GitHub上活跃度很高的对象关系映射框架(ORM)。它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了。

GORM 目前支持的数据库有 MySQL、SQLServer、PostgreSQL、SQLite。

GORM 在使用有以下几点约定:

  • GORM 使用名为 ID 的字段作为主键
  • 如果没有 TableName 函数,使用结构体的蛇形复数作为表名
  • 字段名的蛇形作为列表
  • 使用 CreatedAt、UpdateAt 字段作为创建时间、更新时间

环境准备

如果我们使用的是 mysql 数据库,需要先在项目中下载 gorm 与 mysql 驱动的依赖包:

## 安装gorm
go get -u gorm.io/gorm     
## 安装相应的数据库驱动。GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
go get -u gorm.io/driver/mysql  

连接数据库

package mysql

import (
    "log"
    "time"

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

func main() {
    // 数据库连接
    db, err := gorm.Open(mysql.New(mysql.Config{
        DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
    	DefaultStringSize: 256, // string 类型字段的默认长度                                                                
        DisableDatetimePrecision:  true,  // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
        DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
        DontSupportRenameColumn:   true,  // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
        SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
    }), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }
}

定义模型

在使用 ORM 工具时,通常我们需要在代码中定义模型(Models)与数据库中的数据表进行映射,在 GORM 中模型(Models)通常是正常定义的结构体、基本的 Go 类型或它们的指针。

创建一个 User 类型的结构体,结构体里的字段和 mysql 的 user 表字段一一对应。

type User struct {
    ID uint `gorm:"primarykey"` // 声明主键
    Name string `gorm:"column:name"`
    Age int8 `gorm:"column:age"`
}

gorm.Model 结构体

为了方便模型定义,GORM 内置了一个 gorm.Model 结构体。gorm.Model 是一个包含了ID, CreatedAt, UpdatedAt, DeletedAt 四个字段的 Golang 结构体。

可以将它嵌入到自己的模型中:

type Product struct {
    gorm.Model
    ID uint
    User string `gorm:"column:name"`
    Age uint `gorm:"column:age"`
}

当然也可以完全自定义模型。

自动迁移

GORM 提供了自动迁移功能,可以根据模型结构自动创建或更新数据库表。

func main() {
    // 数据库连接
    db, err := gorm.Open(mysql.New(mysql.Config{
        DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
    	DefaultStringSize: 256, // string 类型字段的默认长度                                                                
        DisableDatetimePrecision:  true,  // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
        DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
        DontSupportRenameColumn:   true,  // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
        SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
    }), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }
    
    // 自动迁移
    err := db.AutoMigrate(&User{})
    if err != nil {
        log.Fatalf("自动迁移失败: %v", err)
    }
    log.Println("自动迁移成功")
}

注意: 如果我们不使用自动迁移,而是自己创建了数据库表,刚才创建的结构体还有一个问题,结构体名为 User,按照约定 GORM 默认会认为表名是 users (小写复数),但我们创建的数据库中的表名为 user,因此要创建一个对应关系,方式为给 User 结构体绑定一个 TableName 方法。

func (u User) TableName() string {
    return "user"
}

增删改查

1. 创建(Create)

我首先尝试了如何使用 GORM 创建一个新用户:

user := User{Name: "wind", Age: 25}
result := db.Create(&user)
if result.Error != nil {
    log.Fatalf("创建用户失败: %v", result.Error)
}
log.Printf("创建用户成功: %v", user)

这里我通过 db.Create() 方法将 User 实例插入到数据库中,result 包含了执行操作的结果。如果创建成功,返回的 user 对象将会包含自动生成的 ID

2. 查询(Read)

(1)根据主键查询
var user User
result := db.First(&user, 1) // 根据主键查询 ID 为 1 的用户
if result.Error != nil {
    log.Fatalf("查询用户失败: %v", result.Error)
}
log.Printf("查询用户成功: %v", user)

使用 db.First() 方法根据主键(ID)查询用户。

(2)根据条件查询
var users []User
result := db.Where("age > ?", 20).Find(&users)
if result.Error != nil {
    log.Fatalf("查询用户失败: %v", result.Error)
}
log.Printf("查询用户成功: %v", users)

通过 db.Where() 方法,我可以根据特定条件查询用户。在这个例子中,我查询了所有年龄大于 20 岁的用户。

更多用法:

// Get all matched records
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';

// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');

// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';

// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;

// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';

// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';

3. 更新(Update)

(1)更新单个字段
result := db.Model(&user).Update("Age", 26)
if result.Error != nil {
    log.Fatalf("更新用户失败: %v", result.Error)
}
log.Println("更新用户年龄成功")

使用 db.Model()Update() 方法可以更新指定字段。在这个例子中,我更新了用户的 Age 字段。

(2)更新多个字段
result := db.Model(&user).Updates(User{Name: "sun", Age: 27})
if result.Error != nil {
    log.Fatalf("更新用户失败: %v", result.Error)
}
log.Println("更新用户信息成功")

如果要更新多个字段,可以直接传递一个结构体给 Updates() 方法。

4. 删除(Delete)

result := db.Delete(&user)
if result.Error != nil {
    log.Fatalf("删除用户失败: %v", result.Error)
}
log.Println("删除用户成功")

使用 db.Delete() 删除指定的记录。在这里,我删除了一个用户。

八、完整代码

将前面的代码整合在一起,完整的 main.go 文件如下:

package main

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

type User struct {
    ID        uint   `gorm:"primaryKey"`
    Name      string
    Age       uint8
}

func main() {
    // 数据库连接
    db, err := gorm.Open(mysql.New(mysql.Config{
        DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
    	DefaultStringSize: 256, // string 类型字段的默认长度                                                                
        DisableDatetimePrecision:  true,  // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
        DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
        DontSupportRenameColumn:   true,  // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
        SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
    }), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }

    // 自动迁移
    err = db.AutoMigrate(&User{})
    if err != nil {
        log.Fatalf("自动迁移失败: %v", err)
    }
    log.Println("自动迁移成功")

    // 创建用户
    user := User{Name: "wind", Age: 25}
    result := db.Create(&user)
    if result.Error != nil {
        log.Fatalf("创建用户失败: %v", result.Error)
    }
    log.Printf("创建用户成功: %v", user)

    // 查询用户
    var queryUser User
    result = db.First(&queryUser, user.ID)
    if result.Error != nil {
        log.Fatalf("查询用户失败: %v", result.Error)
    }
    log.Printf("查询用户成功: %v", queryUser)

    // 更新用户
    result = db.Model(&queryUser).Updates(User{Name: "sun", Age: 27})
    if result.Error != nil {
        log.Fatalf("更新用户失败: %v", result.Error)
    }
    log.Println("更新用户信息成功")

    // 删除用户
    result = db.Delete(&queryUser)
    if result.Error != nil {
        log.Fatalf("删除用户失败: %v", result.Error)
    }
    log.Println("删除用户成功")
}