现象
现在有这么个类需要插入到 MongoDB 中:
@Data @Document(collection = "biz_order") public class BizOrder { @Field private String id; @Field // 其他属性 }
有个操作是根据 BizOrder 的某个字段不同,会往 MongoDB 中插入多条数据,即其他属性都是一样的,只有一个字段不一样。
我的操作是这样的:
BizOrder bizOrder = new BizOrder(); for(int i=0; i<3; i++) { bizOrder.setStatus(i); bizOrderDao.insert(bizOrder); }
然后就会抛异常,由于这个多次插入的概率比较小,一般都是插入一次,所以就没有注意到抛出的异常:
Spring data mongo E11000 duplicate key error collection: index: _id_ dup
后来查询相关资料,发现是 MongoDB 自动生成的 ID 惹的祸。
问题追踪
MongoDB 中的一条记录一般称之为 文档(Document),每个 Document 都有唯一的一个 id,如果你不指定的话,MongoDB 会自动给你生成一条。
现在我们用的 Spring Data MongoDB Template,来看一下源码的 insert 操作是如何进行的。
MongoTemplate API 的 insert 方法:
平平无奇,继续往下看:
还是平平无奇,看到了 doInsert 方法就知道了,真正的插入操作在这里完成的:
最后来看一下 populateIdIfNecessary 这个方法:
所以,当 BizOrder 插入时候的 id 为空的时候,Spring Data Mongo Template 会把 MongoDB 自动生成的 id,赋值到 BizOrder 中。
如果这个时候,你仍然使用这个 BizOrder 对象去插入,那么,MongoDB 会先检查 BizOrder 的 id,结果发现库里已经有了这个 id,所以会抛出重复 id 主键的异常。
结论
解决办法是:用新的对象去插入,或者把 id 的属性置为 null。
妈蛋,坑真多,一旦用了一个新玩意儿,要小心仔细,尽量各个角落都要注意到;慢查询和数据库操作层面的异常都要注意到。