xyxsw
文章35
标签3
分类0
gorm 进阶 | 青训营笔记

gorm 进阶 | 青训营笔记

gorm

关于简单的 gorm 总结 可以看上一篇文章 gorm 初体验 | 青训营笔记

软删除

Gorm提供了软删除的能力,需要在结构体中定义一个Deleted字段,此时再调用Delete删除函数,则会生成update语句,并将deleted字段赋值为当前删除时间。

type Product struct {
    ID      uint
    Code    string
    Price   uint
    Deleted gorm.DeletedAt
}

再次执行删除操作:

go
db.Delete(&Product{}, 1)

生成的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提供了BeginCommitRollback方法用于使用事务。

// 开启事务
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()

在开启事务后,调用增删改操作是应该使用开启事务返回的tx而不是db

Gorm还提供了Transaction函数用于自定提交事务,避免用户漏写CommitRollback

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
}

这种写法,当出现错误时会自动进行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,
})

Gorm生态

Gorm拥有非常丰富的扩展生态,下面列举一些常用的扩展。

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
}

跳过默认事务

为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,您可以在初始化时禁用它。

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  SkipDefaultTransaction: true,
})

命名策略

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
}

默认 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
  },
})

NowFunc

更改创建时间使用的函数

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  NowFunc: func() time.Time {
    return time.Now().Local()
  },
})

DryRun

生成 SQL 但不执行,可以用于准备或测试生成的 SQL

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  DryRun: false,
})

PrepareStmt

PreparedStmt 在执行任何 SQL 时都会创建一个 prepared statement 并将其缓存,以提高后续的效率

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  PrepareStmt: false,
})

禁用嵌套事务

在一个事务中使用 Transaction 方法,GORM 会使用 SavePoint(savedPointName)RollbackTo(savedPointName) 为你提供嵌套事务支持,你可以通过 DisableNestedTransaction 选项关闭它

DisableAutomaticPing

在完成初始化后,GORM 会自动 ping 数据库以检查数据库的可用性,若要禁用该特性,可将其设置为 true

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  DisableAutomaticPing: true,
})

DisableForeignKeyConstraintWhenMigrating

AutoMigrateCreateTable 时,GORM 会自动创建外键约束,若要禁用该特性,可将其设置为 true

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  DisableForeignKeyConstraintWhenMigrating: true,
})
本文作者:xyxsw
本文链接:https://xyxsw.ltd/2023/08/30/gorm%E8%BF%9B%E9%98%B6%20%EF%BD%9C%20%E9%9D%92%E8%AE%AD%E8%90%A5%E7%AC%94%E8%AE%B0/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可