gorm
关于简单的 gorm 总结 可以看上一篇文章 gorm 初体验 | 青训营笔记
软删除
Gorm 提供了软删除的能力,需要在结构体中定义一个 Deleted 字段,此时再调用 Delete 删除函数,则会生成 update 语句,并将 deleted 字段赋值为当前删除时间。
type Product struct {
ID uint
Code string
Price uint
Deleted gorm.DeletedAt
}2
3
4
5
6
再次执行删除操作:
go
db.Delete(&Product{}, 1)2
生成的 sql 为:
UPDATE `product` SET `deleted`='2023-01-30 22:22:22.202' WHERE `product`.`id` = 1 AND `product`.`deleted` IS NULL查询被软删除的操作,要使用 Unscoped 函数:
db.Unscoped().First(&product, 2)有了 DeleteAt 字段后,删除操作已经变成了更新操作,那么想要物理删除怎么办?也是使用 Unscoped 函数:
db.Unscoped().Delete(&Product{}, 1)Gorm 事务
Gorm 提供了 Begin 、 Commit 、 Rollback 方法用于使用事务。
// 开启事务
tx := db.Begin()
if err := tx.Create(&Product{Code: "D32", Price: 100}).Error; err != nil {
// 出现错误回滚
tx.Rollback()
return
}
if err := tx.Create(&Product{Code: "D33", Price: 100}).Error; err != nil {
// 出现错误回滚
tx.Rollback()
return
}
// 提交事务
tx.Commit()2
3
4
5
6
7
8
9
10
11
12
13
14
在开启事务后,调用增删改操作是应该使用开启事务返回的 tx 而不是 db 。
Gorm 还提供了 Transaction 函数用于自定提交事务,避免用户漏写 Commit 、 Rollback 。
if err = db.Transaction(func(tx *gorm.DB) error {
if err := db.Create(&Product{Code: "D55", Price: 100}).Error; err != nil {
return err
}
if err := db.Create(&Product{Code: "D56", Price: 100}).Error; err != nil {
return err
}
return nil
}); err != nil {
return
}2
3
4
5
6
7
8
9
10
11
这种写法,当出现错误时会自动进行 Rollback ,当正常执行时会自动 Commit 。
Hook
当我们想在执行增删改查操作前后做一些额外的操作时,可以使用 Gorm 提供的 Hook 能力。
Hook 是在创建、查询、更新、删除等操作之前、之后自动调用的函数,如果任何 Hook 返回错误, Gorm 将停止后续的操作并回滚事务。
Hook 会开启默认事务,所以会带来了一些性能损失。
性能提高
对于写操作(创建、更新、删除),为了确保数据的完整性, Gorm 会将他们封装在事务内运行,但是这样会降低性能,可以使用 SkipDefaultTransaction 关闭默认事务。
使用 PrepareStmt 缓存预编译语句可以提高后续调用的速度。
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
SkipDefaultTransaction: true,
PrepareStmt: true,
})2
3
4
Gorm 生态
Gorm 拥有非常丰富的扩展生态,下面列举一些常用的扩展。
- 代码生成工具:github.com/go-gorm/gen
- 分片分库方案:github.com/go-gorm/sha…
- 手动索引:github.com/go-gorm/hin…
- 乐观锁:github.com/go-gorm/opt…
- 读写分离:github.com/go-gorm/dbr…
- OpenTelemetry 扩展:github.com/go-gorm/ope…
GORM 配置
GORM 提供的配置可以在初始化时使用
type Config struct {
SkipDefaultTransaction bool
NamingStrategy schema.Namer
Logger logger.Interface
NowFunc func() time.Time
DryRun bool
PrepareStmt bool
DisableNestedTransaction bool
AllowGlobalUpdate bool
DisableAutomaticPing bool
DisableForeignKeyConstraintWhenMigrating bool
}2
3
4
5
6
7
8
9
10
11
12
跳过默认事务
为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,您可以在初始化时禁用它。
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
SkipDefaultTransaction: true,
})2
3
命名策略
GORM 允许用户通过覆盖默认的 NamingStrategy 来更改命名约定,这需要实现接口 Namer
type Namer interface {
TableName(table string) string
SchemaName(table string) string
ColumnName(table, column string) string
JoinTableName(table string) string
RelationshipFKName(Relationship) string
CheckerName(table, column string) string
IndexName(table, column string) string
}2
3
4
5
6
7
8
9
默认 NamingStrategy 也提供了几个选项,如:
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: "t_", // table name prefix, table for `User` would be `t_users`
SingularTable: true, // use singular table name, table for `User` would be `user` with this option enabled
NoLowerCase: true, // skip the snake_casing of names
NameReplacer: strings.NewReplacer("CID", "Cid"), // use name replacer to change struct/field name before convert it to db name
},
})2
3
4
5
6
7
8
NowFunc
更改创建时间使用的函数
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
NowFunc: func() time.Time {
return time.Now().Local()
},
})2
3
4
5
DryRun
生成 SQL 但不执行,可以用于准备或测试生成的 SQL
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
DryRun: false,
})2
3
PrepareStmt
PreparedStmt 在执行任何 SQL 时都会创建一个 prepared statement 并将其缓存,以提高后续的效率
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
PrepareStmt: false,
})2
3
禁用嵌套事务
在一个事务中使用 Transaction 方法,GORM 会使用 SavePoint(savedPointName) , RollbackTo(savedPointName) 为你提供嵌套事务支持,你可以通过 DisableNestedTransaction 选项关闭它
DisableAutomaticPing
在完成初始化后,GORM 会自动 ping 数据库以检查数据库的可用性,若要禁用该特性,可将其设置为 true
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
DisableAutomaticPing: true,
})2
3
DisableForeignKeyConstraintWhenMigrating
在 AutoMigrate 或 CreateTable 时,GORM 会自动创建外键约束,若要禁用该特性,可将其设置为 true
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})2
3