createinbatches是gorm唯一真正可用的批量插入方式,必须传值类型切片(如[]user),batchsize设200–500(mysql)、100–300(pg),返回*gorm.db需检查result.error,空切片安全,错误仅反映最后一批。

CreateInBatches 是 GORM 唯一真正可用的批量插入方式,其他写法要么退化为逐条、要么触发 panic、要么超限失败。
传参必须是值类型切片,不是指针或单结构体
常见 panic 场景:传 &users(其中 users 是 []User)、传 *[]User、或从 JSON 解析后直接用 json.Unmarshal(data, &users) 得到 *[]User 就塞进 CreateInBatches —— 这些都会在运行时报 panic: reflect: call of reflect.Value.Type on zero Value。
正确做法只有这一种:db.CreateInBatches(users, 200),其中 users 是 []User 类型的变量本身(值传递)。
- 别写
for _, u := range users { ptrs = append(ptrs, &u) }→ 所有指针指向同一栈地址,数据全变成最后一项 - 别以为和
Create(&u)一样要取地址 →CreateInBatches接收的是接口值,不是指针接收器 - 空切片安全:
len(users) == 0时不会发 SQL,result.Error == nil
batchSize 设多少才不崩又够快
设太小(如 1 或 10)等于白用:函数调用开销还在,SQL 仍是单条,吞吐没提升;设太大(如 1000+)容易撞上 MySQL 的 max_allowed_packet(默认 4MB),报错 ERROR 1153 (08S01): Got a packet bigger than 'max_allowed_packet' bytes 或 MySQL server has gone away。
推荐实测区间:
- MySQL:
200–500(单行约 2KB 时,500 行 ≈ 1MB,留足余量) - PostgreSQL:
100–300(若关掉RETURNING可放宽到 500) - SQLite:
慎用—— 底层不支持多值 INSERT,GORM 自动降级为循环单条,此时不如直接db.Exec()拼接
错误检查必须手动,且不能只看最后一批
CreateInBatches 返回的是 *gorm.DB,不是 error。写 if err := db.CreateInBatches(...) 直接编译失败。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
正确检查方式:
result := db.CreateInBatches(users, 200)
if result.Error != nil {
// 注意:这只代表最后一批失败
// 前面所有成功批次已写入数据库,不会回滚
}
想捕获每批错误?只能自己分片 + 循环调用:
- 用
for i := 0; i - 每批单独
tx.CreateInBatches(chunk, 200)并检查result.Error - 外部包事务:
db.Transaction(func(tx *gorm.DB) error { ... }),否则部分失败就脏数据
主键和时间戳不会自动回填,别信文档默认行为
MySQL 完全不支持 RETURNING,插完 users[0].ID 还是 0;PostgreSQL 虽支持,但只填第一批的 ID —— 你传 1000 条、batchSize=200,共 5 批,只有前 200 条的 ID 字段被更新,其余 800 条保持零值。
CreatedAt/UpdatedAt 字段在批量插入时也不会自动填充,除非你显式赋值,或数据库字段定义了 DEFAULT CURRENT_TIMESTAMP。
如果业务强依赖插入后的 ID(比如后续关联写入),就别用 CreateInBatches,改用单条 Create 或原生 *sql.DB + LAST_INSERT_ID() 配合 ROW_COUNT() 推算(但有竞态风险)。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










