GORM 最佳实践(2) | 青训营笔记

839 阅读5分钟

GORM 最佳实践(2) | 青训营笔记

  • 数据序列化与 SQL 表达式
  • 批量数据操作
  • 代码复用、分库分表、Sharding
  • 混沌工程/压测
  • Logger/Trace
  • Migrator
  • Gen 代码生成/Raw SQL
  • 安全

批量数据操作 - 批量创建/查询

var users = []User{{Name: "hzhan1"},{Name: "hzhan2"}, {Name: "hzhan3"}}
db.Create(&users)
db.CreateInBatches(users, 100)
for _,user := range users{
    user.ID
}

rows, err := db.Model(&User{}).Where("role = ?", "admin").Rows()
for row.Next() {
    row.Scan(&name, &age, &email)
    db.ScanRows(rows, &user)
}

DB.Where("role = ?", "admin").FindInBatches(&results, 100, func(tx *gorm.DB, batch int)error){
})

创建一个空的用户数组users,并添加三个用户 使用db.Create将数组中的用户保存到数据库中。

使用db.CreateInBatches批量将用户保存到数据库中,每批大小为100

使用db.Model查询所有角色为"admin"的用户,结果存储在rows中。

批量查询方法1,使用row.Scan将行中的每个字段赋值给对应变量。

批量查询方法2,使用db.ScanRows将行扫描到user中。

使用DB.Where查询所有角色为"admin"的结果,以每批100的大小使用FindInBatches分批处理,回调函数中对每个批次进行处理。

批量数据操作 - 批量更新

db.Clauses(clause.OnConglict{DoNothing: true}).Create(&users)

db.Clauses(clause.Insert{Modifier: "IGNORE"}).Create(&users)

db.Clauses(clause.OnConflict{
    Columns: []clause.Column{{Name: "id"}},
    DoUpdates: clause.Assignments(map[string]interface{}{"deleted_at": nil}),
}).Create(&users)

db.Clauses(clause.OnConflict{
    Columns: []clause.Column{{Name: "id"}},
    DoUpdates: clause.Assignments(map[string]interface{}{"count": gorm.Expr("GREATEST(count, VALUES(count))")}),
}).Create(&users)

db.Clauses(clause.OnConflict{
    Columns: []clause.Column{{Name: "id"}},
    DoUpdates: clause.AssignmentConlumns([]string{"name", "age"}),
}).Create(&users)

db.Clauses(clause.OnConflict{UpdateAll: true}).Create(&users)

这些代码都是使用GORM库对数据库进行操作的方式。具体来说,这些代码都是对于Create方法进行了不同的参数配置和限制。

  1. 在执行创建操作时如果出现重复数据,则不插入该数据,而是直接跳过;
  2. 在执行创建操作时如果出现重复数据,则忽略该数据,不进行插入,而不是跳过;
  3. 表示在执行创建操作时如果出现重复数据,则更新该数据行中的删除时间(将其设为 NULL),达到恢复删除的效果;
  4. 在执行创建操作时如果出现重复数据,则更新该数据行中的count字段为新数据行中的count和原数据行中的count的最大值;
  5. 在执行创建操作时如果出现重复数据,则更新该数据行中的nameage字段为新数据行中的值;
  6. 在执行创建操作时如果出现重复数据,则更新该数据行中的所有字段为新数据行中的值,对于存在的字段进行替换。

批量数据操作 - 批量数据加速操作

方法1: 关闭默认事务
db.err := gorm.Open(sqlite.Open("gorm.db"),&gorm.Config{
    SkipDefaultTransaction: true,
})
db.Create(&user)

tx := db.Session(&Session{SkipDefaultTransaction: true})
tx.Create(&user)

这段代码使用GORM库连接到SQLite数据库,并使用SkipDefaultTransaction配置选项创建了一个gorm.DB对象。它通过Create方法向数据库插入了一个用户记录。使用Session方法创建了一个新的数据库会话,并将SkipDefaultTransaction配置选项设置为true,以跳过默认的数据库事务。然后,在此会话中通过Create方法再次插入了一个用户记录。这个操作会在一个新的事务中进行,并且不会受到之前的操作的影响。

方法2: 默认批量导入会调用 Hooks 方法,使用 SkipHooks 跳过
DB.Session(&gorm.Session{SkipHook: true}).Create(&users)
DB.Session(&gorm.Session{SkipHook: true}).CreateInBatches(users, 1000)

使用DB.Session(&gorm.Session{SkipHook: true})创建一个数据库会话对象,并设置SkipHook属性为 true,这将跳过所有gorm钩子(hook)的执行。

通过Create方法,将users对象写入数据库,当写入对象数量较大时,可以使用CreateInBatches方法将数据分批写入数据库。

当写入对象数量较大时,分批写入可以减少内存占用,提高写入效率。在本例中,每次写入的批次大小为1000

方法3: 使用 Prepared Statement
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{PrepareStmt: true})
db.Create(&users)

gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{PrepareStmt: true})打开一个SQLite数据库连接,将连接信息传递给GORM库的Open方法。这个连接信息指定了数据库的名称为"gorm.db"

&gorm.Config{PrepareStmt: true}指定GORM库的配置,其中PrepareStmt: true表示启用预处理语句。

db.Create(&users)使用先前打开的数据库连接创建一个新记录。该记录的数据来源于一个名为"users"的变量,这个变量的类型可能是一个结构体,也可能是一个切片。如果是结构体,那么每个字段的值将作为该记录的一个属性;如果是切片,则每个元素将被视为一个结构体,并成为一个新记录的属性。在这里,我们不能确定"users"的类型,但是我们可以确认它包含多个记录,否则我们可能会直接使用db.Create(&user)来创建单个记录。

混合使用
tx := db.Session(&Session{
    PrepareStmt: true, SkipDefaultTransaction: true, SkipHookL true, CreateBatchSize: 1000,
})
tx.Create(&users)

tx := db.Session(&Session{...})使用db对象创建一个新的会话(Session),返回会话对象txSessiongorm包中的一个结构体,其包含了很多配置项,如本代码片段中的PrepareStmt表示是否开启预编译、SkipDefaultTransaction表示是否跳过默认事务、SkipHook表示是否跳过钩子函数、CreateBatchSize表示批量操作时每个批次的大小等等。 tx.Create(&users)在会话对象tx中,使用Create方法向数据库中添加users这个模型对象。Create方法由gorm包提供,其作用是创建并保存一个新的对象到数据库中。