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方法进行了不同的参数配置和限制。
- 在执行创建操作时如果出现重复数据,则不插入该数据,而是直接跳过;
- 在执行创建操作时如果出现重复数据,则忽略该数据,不进行插入,而不是跳过;
- 表示在执行创建操作时如果出现重复数据,则更新该数据行中的删除时间(将其设为 NULL),达到恢复删除的效果;
- 在执行创建操作时如果出现重复数据,则更新该数据行中的
count字段为新数据行中的count和原数据行中的count的最大值; - 在执行创建操作时如果出现重复数据,则更新该数据行中的
name和age字段为新数据行中的值; - 在执行创建操作时如果出现重复数据,则更新该数据行中的所有字段为新数据行中的值,对于存在的字段进行替换。
批量数据操作 - 批量数据加速操作
方法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),返回会话对象tx。Session是gorm包中的一个结构体,其包含了很多配置项,如本代码片段中的PrepareStmt表示是否开启预编译、SkipDefaultTransaction表示是否跳过默认事务、SkipHook表示是否跳过钩子函数、CreateBatchSize表示批量操作时每个批次的大小等等。
tx.Create(&users)在会话对象tx中,使用Create方法向数据库中添加users这个模型对象。Create方法由gorm包提供,其作用是创建并保存一个新的对象到数据库中。