CreateInBatches是GORM官方唯一推荐的批量插入方式,必须传切片、batchSize>1,返回*gorm.DB需通过result.Error检查错误,空切片安全,但默认不开启事务,ID回填在MySQL下不可靠。

CreateInBatches 是唯一能用的批量插入方式
别试 db.Create(&users) 或 db.Create(users),GORM v2 默认仍是一条一条插,1000 条就发 1000 次 SQL。真正能批量的只有 CreateInBatches —— 它是 GORM 官方封装的、带分批逻辑和预编译复用的唯一推荐路径。
必须传切片([]User),不能传指针切片(*[]User)或单个 struct;batchSize 必须 > 1,设成 1 就退化为循环插入,还多一层函数调用开销。
-
CreateInBatches返回的是*gorm.DB,不是error,写if err := db.CreateInBatches(...)直接编译失败 - 正确检查方式:
result := db.CreateInBatches(users, 100); if result.Error != nil { ... } - 空切片安全:
users为空时,不发任何 SQL,result.RowsAffected == 0且result.Error == nil
事务必须显式包裹,否则部分失败会脏数据
CreateInBatches 默认不开启事务。某一批插入失败(比如主键冲突、字段超长),前面已成功的批次早已提交,数据库状态不一致 —— 这在迁移脚本或后台任务里极易引发线上问题。
正确做法是手动开事务:
tx := db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.CreateInBatches(users, 100).Error; err != nil {
tx.Rollback()
return err
}
return tx.Commit().Error
- 别依赖
RowsAffected判断是否全量成功:它返回总行数,出错时可能部分成功、部分失败 - 若用 Gin 处理 HTTP 请求,建议把事务生命周期控制在 handler 内,避免跨请求泄露
- MySQL 下事务内大量插入需留意
max_allowed_packet,batchSize 建议控制在 100–500
ID 和时间字段回填不可靠,尤其 MySQL 场景
插入后打印 users[0].ID 还是 0?这不是代码写错了,是 MySQL 本身不支持 RETURNING,GORM 无法从单条 INSERT INTO VALUES(),() 语句中拿到自增 ID。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
PostgreSQL 虽支持 clause.Returning,但 CreateInBatches 对它的兼容性弱:v2.2+ 仍不稳定,多批次时返回结果易错位。
- MySQL 下别指望 ID 回填,后续查库要用其他字段(如唯一业务码)定位
-
CreatedAt/UpdatedAt字段会自动赋值(前提是结构体字段类型为time.Time或嵌入gorm.Model),无需手动设置 - 若结构体含
json:"-" gorm:"autoCreateTime",可防 JSON 解析覆盖,但注意别同时写json:"created_at"和gorm:"column:created_at",容易冲突
Gin 中整合时最容易静默失败的三件事
HTTP 接口接收批量数据后调用 CreateInBatches,看似流程完整,但以下三点不做处理,数据就“消失”了也不报错:
- 没校验
result.RowsAffected:字段溢出(如uint8存 200)、唯一索引冲突时,result.Error可能为nil,但RowsAffected == 0 - 没做业务字段校验:前端传空字符串或非法邮箱,GORM 不拦,只管写,得靠
validator提前拦截 - 没禁用软删除干扰:如果模型启用了软删除,
CreateInBatches插入时会默认设DeletedAt = nil,但若字段被 JSON 覆盖或结构体初始化错误,可能误设为非零值导致记录不可见
批量插入不是“丢进去就完事”,每一步都有默认行为陷阱,尤其是错误检查和字段控制,漏掉任何一个,数据就卡在半路。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










