推荐优先使用mongoTemplate.getCollection("xxx").insertMany(),因其贴近原生语义、集合名可控、避免注解依赖和序列化开销,并返回插入结果便于幂等校验。

直接用 mongoTemplate.insert() 或 mongoTemplate.insertMany(),但必须注意:前者默认写入固定集合名,后者更可控、更接近原生语义,且能规避部分隐式行为。
用 mongoTemplate.insert(List, Class) 时,集合名从哪来?
这个重载方法会自动推导集合名:取 User.class 上的 @Document(collection = "users") 注解值;若没注解,则用类名小写(user)。一旦实体类没加 @Document,又恰好类名是驼峰(如 UserOrder),就会变成 userorder —— 很可能不是你实际想写的集合。
- 务必在实体类上显式声明
@Document(collection = "your_collection_name") - 如果运行时要动态切集合(比如按月分表),别用这个方法,它不支持传入集合名参数
- 该方法内部仍调用的是 driver 的
insertMany(),但封装层做了额外判断(如是否带_id),异常堆栈可能掩盖真实原因
为什么推荐优先用 mongoTemplate.getCollection("xxx").insertMany()?
这是最贴近 MongoDB 原生批量写入语义的方式,绕过 Spring Data 的元数据解析和类型转换逻辑,适合对性能、控制力或兼容性有要求的场景。
- 集合名完全由你控制,不怕注解缺失或命名规则误判
- 可传入
List<Document>,避免实体类序列化开销(尤其字段多、嵌套深时) - 返回结果含
wasAcknowledged()和插入的ObjectId列表,便于做幂等校验或后续关联 - 注意:
Document中若没显式设_id,driver 会自动生成;而实体类若没配@Id字段,insert()可能抛IllegalArgumentException
save() 和 insert() 混用会导致主键冲突异常
如果你在同一批数据里混用 save()(更新/插入语义)和 insert()(仅插入),或者数据中部分文档带 _id、部分不带,极大概率触发 DuplicateKeyException。
-
save()对单个文档是“upsert”,但save(List)并不存在 —— Spring Data 不提供批量 save 方法 - 所谓“批量 upsert”,得用
mongoTemplate.upsert()配合UpdateOptions.upsert(true),且需逐条构造Query+Update - 真正需要“存在则更新、不存在则插入”的批量场景,应改用
bulkOps(见下一条)
大批量(>1w 条)写入必须用 bulkOps 控制批次和错误策略
直接传 5w 条进 insertMany(),MongoDB 默认单次请求上限是 1000 条(取决于 maxWriteBatchSize 和 BSON 大小),超限会报 CommandFailedException 或截断。
- 用
mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, "col")显式分批,例如每 1000 条提交一次 -
UNORDERED模式下失败项不影响其余项;ORDERED模式遇到第一个失败就终止 - 可调用
execute()后检查BulkWriteResult.getInsertedCount()、getUpsertedCount()等确认实际效果 - 别忽略网络超时:在
application.yml中加大spring.data.mongodb.socket-timeout和connect-timeout
最容易被忽略的是:批量操作不保证原子性,即使用了 UNORDERED,也得自己处理部分失败后的补偿逻辑;另外,事务只对单文档操作有效,跨文档批量写入无法回滚。


















