go语言Gin框架的学习路线(九)

GORM的CRUD教程

CRUD 是 "Create, Read, Update, Delete"(创建、读取、更新、删除)的缩写,代表了数据库操作的基本功能。在 GORM 的上下文中,CRUD 指的是使用 GORM 库来执行这些基本的数据库操作。

创建的

在 GORM 中创建记录通常使用 Create 方法。以下是一个创建记录的例子,包括定义模型、创建记录以及处理数据库连接:

步骤 1: 定义模型

首先,定义一个模型,通常是一个结构体,它内嵌了 GORM 的 Model 结构体来获得自动管理的字段,如 IDCreatedAtUpdatedAt 等。

type User struct {
    gorm.Model
    Name    string
    Age     int
    Email   string
}

步骤 2: 连接数据库

然后,使用 GORM 连接到数据库。这里以 SQLite 为例,实际使用中可以是 MySQL、PostgreSQL、SQL Server 等。

import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "log"
)

func main() {
    dsn := "file:db.sqlite?mode=memory&cache=shared&_fk=1"
    db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
    if err != nil {
        log.Fatal("Could not connect to the database", err)
    }
    defer db.Close()

步骤 3: 自动迁移

使用 AutoMigrate 方法来自动创建或更新数据库表,以匹配模型的结构。

// 自动迁移模式,确保数据库结构与 User 结构体一致
db.AutoMigrate(&User{})

步骤 4: 创建记录

使用 Create 方法创建新的记录。

    // 创建新用户
    user := User{Name: "Alice", Age: 30, Email: "alice@example.com"}
    db.Create(&user) // 使用 &user 因为 Create 需要指针来设置返回值
}

在这个例子中,Create 方法将 User 结构体的实例插入到数据库中。CreatedAt 字段会自动设置为当前时间戳,表示记录被创建的时间。如果记录成功创建,user 变量也会被更新,包括数据库生成的主键 ID

完整示例代码
package main

import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "log"
)

type User struct {
    gorm.Model
    Name  string
    Age   int
    Email string
}

func main() {
    dsn := "file:db.sqlite?mode=memory&cache=shared&_fk=1"
    db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
    if err != nil {
        log.Fatal("Could not connect to the database", err)
    }
    defer db.Close()

    db.AutoMigrate(&User{})

    user := User{Name: "Alice", Age: 30, Email: "alice@example.com"}
    db.Create(&user) // 注意 Create 方法需要一个指针

    // 打印创建的用户信息
    log.Printf("Created User: %+v\n", user)
}

**这个示例程序展示了如何使用 GORM 来定义一个模型、连接到数据库、自动创建表结构、插入一条新记录,并打印这条记录。**这整个过程就是数据库操作中的 "Create"(创建)部分。通过 GORM,你可以用面向对象的方式来处理数据库的 CRUD 操作,而不需要编写复杂的 SQL 语句。

在 GORM 中,创建记录时可以结合多种方式来设置默认值。

以下是一些方法和示例,展示如何在创建记录时使用这些功能:

1. 使用 Tag 定义字段的默认值

你可以在结构体字段的 tag 中使用 default 关键字来指定默认值。

type User struct {
    gorm.Model
    Name    string
    Age     int    `gorm:"default:30"`
    Email   string `gorm:"default:'noreply@example.com'"`
}

在这个例子中,如果创建 User 时没有指定 AgeEmail,它们将自动被设置为 30'noreply@example.com'

GORM 的 tag 允许你为字段指定额外的选项,这些选项在数据库迁移、记录创建和查询时会被 GORM 识别和使用。在这个例子中,default 关键字用于指定字段的默认值。

2. 使用指针方式实现零值存入数据库

通过使用指针类型,你可以在创建记录时将某些字段设置为 SQL 的 NULL 值。

type User struct {
    gorm.Model
    Name string
    Bio  *string
}

如果 Bionil,它在数据库中将被存储为 NULL。

  • Bio: 一个指向字符串的指针 *string。这里的指针用法有几个目的:

    • NULL 值 : 在 SQL 中,指针可以用来表示 NULL 值。如果 Bio 被初始化为 nil,那么在数据库中对应的字段将存储为 NULL。
    • 可选字段 : 指针表示这个字段是可选的,不是必须的。只有当 Bio 被赋予了一个非 nil 的值时,才会在数据库中存储一个实际的字符串。
    • 空字符串与 NULL 的区分 : 如果字段是 *string 类型,你可以区分空字符串 ("") 和 NULL。空字符串是一个有效的字符串值,而 NULL 表示字段没有值。

3. 使用 Scanner/Valuer 接口方式实现零值存入数据库

通过实现 sql.Scannerdriver.Valuer 接口,你可以控制字段的默认行为。

type CustomType struct {
    value string
}

func (ct *CustomType) Scan(src interface{}) error {
    var str string
    err := sql.Scan(src, &str)
    if err != nil {
        return err
    }
    ct.value = str
    return nil
}

func (ct CustomType) Value() (driver.Value, error) {
    if ct.value == "" {
        return "default_value", nil // 返回默认值
    }
    return ct.value, nil
}

type User struct {
    gorm.Model
    Name string
    Data CustomType `gorm:"not null"`
}

在这个例子中,如果 Data.value 是空字符串,Value() 方法将返回 "default_value" 作为默认值。

代码理解

  • CustomTypeScan 方法允许你在从数据库检索记录时自定义如何将数据库中的值(通常是字符串)转换为 CustomType 类型的字段。
  • CustomTypeValue 方法允许你在将记录保存到数据库时自定义如何将 CustomType 类型的字段转换为数据库中的值。在这个例子中,如果 value 是空字符串,它将使用 "default_value" 作为默认值。
  • User 结构体中的 Data 字段使用 CustomType,这意味着在数据库操作中,Data 字段的值将通过 CustomTypeScanValue 方法进行转换。
  • gorm:"not null" tag 指示 GORM 在数据库中对应的列不允许为空值。如果使用 Data 作为字段类型,你需要确保在调用 Value 方法时总是返回一个非 NULL 的数据库值。

示例应用

这个自定义类型和接口实现可以用于处理那些需要特殊处理的数据库字段,例如,加密字段、格式特定的字段,或者像这里的示例,当数据库字段可能为空但你想提供一个默认值的情况。

4. 扩展创建选项

你可以使用 Create 方法的选项来自定义创建行为,例如设置默认值。

user := User{Name: "Alice"}
db.Clauses(clause.Expr{SQL: "SET", Name: "age", Value: 30}).Create(&user)

在这个例子中,Clauses 方法用于添加额外的 SQL 表达式,这里使用了 SET 来在创建记录时设置 age 字段的默认值。

5. 使用钩子(Hooks)

GORM 提供了钩子(如 BeforeCreate),在创建记录之前可以设置默认值。

type User struct {
    gorm.Model
    Name string
    Age  int
}

func (u *User) BeforeCreate(tx *gorm.DB) error {
    if u.Age == 0 {
        u.Age = 30 // 如果 Age 是 0,设置默认值
    }
    return nil
}

在这个例子中,BeforeCreate 钩子会在记录被创建之前被调用,允许你在记录保存到数据库之前修改字段值。

  • BeforeCreate 是一个在 GORM 创建记录之前自动调用的方法。它是一个钩子,允许你在记录被保存到数据库之前执行自定义逻辑。
  • 这个方法接收一个 *gorm.DB 类型的参数 tx,表示当前的数据库事务。
  • 在这个钩子中,如果 UserAge 字段值为 0,它将 Age 设置为 30 作为默认值。
  • 钩子函数返回 nil 表示没有错误,允许 GORM 继续执行创建记录的操作。

钩子的工作流程

  1. 当你调用 db.Create(&user) 来创建一个新的 User 记录时,GORM 会触发 BeforeCreate 钩子。
  2. BeforeCreate 钩子中,你可以访问和修改 user 的字段,例如在这里检查 Age 是否为 0 并设置默认值。
  3. 钩子函数执行完毕后,GORM 会根据修改后的 user 字段的值创建数据库记录。

示例应用

这个钩子非常有用,特别是当你需要在记录保存到数据库之前执行一些验证、计算或设置默认值时。通过使用钩子,你可以保持你的业务逻辑和数据访问代码分离,同时确保数据的一致性和完整性。

注意

  • 钩子函数应该总是返回 nil 错误,除非你想要阻止 GORM 继续执行创建操作。
  • 钩子函数提供了对当前事务的引用,这意味着你可以在钩子中执行额外的数据库操作,例如查询或更新其他记录。

通过这种方式,GORM 的钩子提供了一个强大的机制来自定义数据库操作流程。

在 GORM 中,钩子(Hooks)的作用是在特定的数据库操作事件之前或之后自动执行代码。这些钩子可以视为拦截器或回调函数,它们允许你在 GORM 执行标准 CRUD 操作的过程中插入自定义逻辑。

钩子的主要作用包括:

  1. 设置默认值

    • 在创建或更新记录之前,可以设置字段的默认值。
  2. 验证数据

    • 在创建或更新记录之前,可以验证数据的有效性,如果数据不符合要求,可以返回错误以中断操作。
  3. 修改字段值

    • 在记录保存到数据库之前,可以修改字段的值,例如,对密码进行加密处理。
  4. 执行额外的数据库操作

    • 可以在创建或更新记录的同时,执行其他数据库操作,如更新相关联的记录或触发级联删除。
  5. 记录操作日志

    • 可以在记录被创建或更新后,记录操作日志,用于审计或调试。
  6. 处理关联关系

    • 在创建或更新具有关联关系(如外键)的记录时,可以手动处理这些关系。
  7. 自动填充字段

    • 可以在记录创建或更新后,自动填充某些字段,如根据当前时间自动设置时间戳。
  8. 事务管理

    • 可以在钩子中执行事务的提交或回滚操作,以确保数据的一致性。

可用的钩子类型:

  • BeforeCreate(): 在记录创建之前调用。
  • AfterCreate(): 在记录创建之后调用。
  • BeforeSave(): 在记录保存之前调用,适用于创建和更新操作。
  • AfterSave(): 在记录保存之后调用,适用于创建和更新操作。
  • BeforeUpdate(): 在记录更新之前调用。
  • AfterUpdate(): 在记录更新之后调用。
  • BeforeDelete(): 在记录删除之前调用。
  • AfterDelete(): 在记录删除之后调用。
  • BeforeFind(): 在记录查询之前调用。
  • AfterFind(): 在记录查询之后调用。

示例代码

结合以上方法,以下是一个完整的示例,展示如何在创建记录时使用这些功能:

package main

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

type CustomType struct {
    value string
}

func (ct *CustomType) Scan(src interface{}) error {
    var str string
    err := sql.Scan(src, &str)
    if err != nil {
        return err
    }
    ct.value = str
    return nil
}

func (ct CustomType) Value() (driver.Value, error) {
    if ct.value == "" {
        return "default_value", nil // 返回默认值
    }
    return ct.value, nil
}

type User struct {
    gorm.Model
    Name    string
    Age     int           `gorm:"default:30"`
    Bio     *string
    Data    CustomType    `gorm:"not null"`
}

func (u *User) BeforeCreate(tx *gorm.DB) error {
    if u.Age == 0 {
        u.Age = 30 // 如果 Age 是 0,设置默认值
    }
    if u.Bio == nil {
        u.Bio = new(string)
        *u.Bio = "This is a bio"
    }
    return nil
}

func main() {
    dsn := "file:db.sqlite?mode=memory&cache=shared&_fk=1"
    db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
    if err != nil {
        log.Fatal("Could not connect to the database", err)
    }
    defer db.Close()

    db.AutoMigrate(&User{})

    user := User{Name: "Alice", Bio: nil}
    db.Create(&user)

    var createdUser User
    db.First(&createdUser, "name = ?", "Alice")
    log.Printf("Created User: %+v\n", createdUser)
}

期末放假自学Gin框架,希望我们可以一起学习!

相关推荐
慕城南风1 小时前
Go语言中的defer,panic,recover 与错误处理
golang·go
eybk2 小时前
Pytorch+Mumu模拟器+萤石摄像头实现对小孩学习的监控
学习
6.942 小时前
Scala学习记录 递归调用 练习
开发语言·学习·scala
守护者1703 小时前
JAVA学习-练习试用Java实现“使用Arrays.toString方法将数组转换为字符串并打印出来”
java·学习
学会沉淀。3 小时前
Docker学习
java·开发语言·学习
Rinai_R4 小时前
计算机组成原理的学习笔记(7)-- 存储器·其二 容量扩展/多模块存储系统/外存/Cache/虚拟存储器
笔记·物联网·学习
吃着火锅x唱着歌4 小时前
PHP7内核剖析 学习笔记 第四章 内存管理(1)
android·笔记·学习
ragnwang4 小时前
C++ Eigen常见的高级用法 [学习笔记]
c++·笔记·学习
Web阿成5 小时前
3.学习webpack配置 尝试打包ts文件
前端·学习·webpack·typescript