GORM的CRUD教程
CRUD 是 "Create, Read, Update, Delete"(创建、读取、更新、删除)的缩写,代表了数据库操作的基本功能。在 GORM 的上下文中,CRUD 指的是使用 GORM 库来执行这些基本的数据库操作。
创建的
在 GORM 中创建记录通常使用 Create
方法。以下是一个创建记录的例子,包括定义模型、创建记录以及处理数据库连接:
步骤 1: 定义模型
首先,定义一个模型,通常是一个结构体,它内嵌了 GORM 的 Model
结构体来获得自动管理的字段,如 ID
、CreatedAt
、UpdatedAt
等。
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
时没有指定 Age
和 Email
,它们将自动被设置为 30
和 'noreply@example.com'
。
GORM 的 tag 允许你为字段指定额外的选项,这些选项在数据库迁移、记录创建和查询时会被 GORM 识别和使用。在这个例子中,default
关键字用于指定字段的默认值。
2. 使用指针方式实现零值存入数据库
通过使用指针类型,你可以在创建记录时将某些字段设置为 SQL 的 NULL 值。
type User struct {
gorm.Model
Name string
Bio *string
}
如果 Bio
为 nil
,它在数据库中将被存储为 NULL。
-
Bio
: 一个指向字符串的指针*string
。这里的指针用法有几个目的:- NULL 值 : 在 SQL 中,指针可以用来表示 NULL 值。如果
Bio
被初始化为nil
,那么在数据库中对应的字段将存储为 NULL。 - 可选字段 : 指针表示这个字段是可选的,不是必须的。只有当
Bio
被赋予了一个非nil
的值时,才会在数据库中存储一个实际的字符串。 - 空字符串与 NULL 的区分 : 如果字段是
*string
类型,你可以区分空字符串 (""
) 和 NULL。空字符串是一个有效的字符串值,而 NULL 表示字段没有值。
- NULL 值 : 在 SQL 中,指针可以用来表示 NULL 值。如果
3. 使用 Scanner/Valuer 接口方式实现零值存入数据库
通过实现 sql.Scanner
和 driver.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"
作为默认值。
代码理解
CustomType
的Scan
方法允许你在从数据库检索记录时自定义如何将数据库中的值(通常是字符串)转换为CustomType
类型的字段。CustomType
的Value
方法允许你在将记录保存到数据库时自定义如何将CustomType
类型的字段转换为数据库中的值。在这个例子中,如果value
是空字符串,它将使用"default_value"
作为默认值。User
结构体中的Data
字段使用CustomType
,这意味着在数据库操作中,Data
字段的值将通过CustomType
的Scan
和Value
方法进行转换。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
,表示当前的数据库事务。 - 在这个钩子中,如果
User
的Age
字段值为0
,它将Age
设置为30
作为默认值。 - 钩子函数返回
nil
表示没有错误,允许 GORM 继续执行创建记录的操作。
钩子的工作流程
- 当你调用
db.Create(&user)
来创建一个新的User
记录时,GORM 会触发BeforeCreate
钩子。 - 在
BeforeCreate
钩子中,你可以访问和修改user
的字段,例如在这里检查Age
是否为0
并设置默认值。 - 钩子函数执行完毕后,GORM 会根据修改后的
user
字段的值创建数据库记录。
示例应用
这个钩子非常有用,特别是当你需要在记录保存到数据库之前执行一些验证、计算或设置默认值时。通过使用钩子,你可以保持你的业务逻辑和数据访问代码分离,同时确保数据的一致性和完整性。
注意
- 钩子函数应该总是返回
nil
错误,除非你想要阻止 GORM 继续执行创建操作。 - 钩子函数提供了对当前事务的引用,这意味着你可以在钩子中执行额外的数据库操作,例如查询或更新其他记录。
通过这种方式,GORM 的钩子提供了一个强大的机制来自定义数据库操作流程。
在 GORM 中,钩子(Hooks)的作用是在特定的数据库操作事件之前或之后自动执行代码。这些钩子可以视为拦截器或回调函数,它们允许你在 GORM 执行标准 CRUD 操作的过程中插入自定义逻辑。
钩子的主要作用包括:
-
设置默认值:
- 在创建或更新记录之前,可以设置字段的默认值。
-
验证数据:
- 在创建或更新记录之前,可以验证数据的有效性,如果数据不符合要求,可以返回错误以中断操作。
-
修改字段值:
- 在记录保存到数据库之前,可以修改字段的值,例如,对密码进行加密处理。
-
执行额外的数据库操作:
- 可以在创建或更新记录的同时,执行其他数据库操作,如更新相关联的记录或触发级联删除。
-
记录操作日志:
- 可以在记录被创建或更新后,记录操作日志,用于审计或调试。
-
处理关联关系:
- 在创建或更新具有关联关系(如外键)的记录时,可以手动处理这些关系。
-
自动填充字段:
- 可以在记录创建或更新后,自动填充某些字段,如根据当前时间自动设置时间戳。
-
事务管理:
- 可以在钩子中执行事务的提交或回滚操作,以确保数据的一致性。
可用的钩子类型:
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框架,希望我们可以一起学习!