美文网首页
E11000 duplicate key error colle

E11000 duplicate key error colle

作者: 秋元_92a3 | 来源:发表于2020-10-14 16:52 被阅读0次

问题

这个一个springboot中使用spring-boot-starter-data-mongodb操作mongo时遇到的错误。完整的错误日志如下:

org.springframework.dao.DuplicateKeyException: E11000 duplicate key error collection: pile.Collection1 index: _id_ dup key: { _id: ObjectId('5f862a618b85fa49eff8789e') }; nested exception is com.mongodb.MongoWriteException: E11000 duplicate key error collection: pile.Collection1 index: _id_ dup key: { _id: ObjectId('5f862a618b85fa49eff8789e') }
    at org.springframework.data.mongodb.core.MongoExceptionTranslator.translateExceptionIfPossible(MongoExceptionTranslator.java:99)
    at org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException(MongoTemplate.java:2874)
    at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:568)
    at org.springframework.data.mongodb.core.MongoTemplate.insertDocument(MongoTemplate.java:1436)
    at org.springframework.data.mongodb.core.MongoTemplate.doInsert(MongoTemplate.java:1236)
    at org.springframework.data.mongodb.core.MongoTemplate.insert(MongoTemplate.java:1168)
    at com.fp.chargepile.server.MainServerHandler.channelRead(MainServerHandler.java:204)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
    at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:287)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:624)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:559)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:476)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:438)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)
    at java.lang.Thread.run(Thread.java:748)

这个错误是在使用mongoTemplate做insert操作的时候发生的,造成的影响是插入第一条数据的时候OK,插入第二条数据的时候报这个错误,同事发生这个错误的数据插入失败。

发生背景

这个错误发生于迁移的时候,之前项目操作mongo使用的mongo-client,进行操作的,在mongo集合的实体使用的ObjectId类型声明了下主键_id,使用mongo-client进行insert的时候,实体中_id字段不赋值,插入的时候client会自动处理主键字段,实现正确的插入,使用mongo-client进行插入的代码如下:

public void insert(T t) {
        MongoClient mongoClient = null;
        try {
            String user = NacosConfigLoad.getMongoUser();
            String databaseName = NacosConfigLoad.getMongoDatabase();
            char[] password = NacosConfigLoad.getMongoPassword().toCharArray();

            MongoCredential credential = MongoCredential.createCredential(user, databaseName, password);
            mongoClient = MongoClients.create(
                    MongoClientSettings.builder()
                            .applyToClusterSettings(builder ->
                                    builder.hosts(Arrays.asList(new ServerAddress(NacosConfigLoad.getMongoUrl(), NacosConfigLoad.getMongoPort()))))
                            .credential(credential)
                            .build());
            // create codec registry for POJOs
            CodecRegistry pojoCodecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(),
                    fromProviders(PojoCodecProvider.builder().automatic(true).build()));

            // get handle to "mydb" database
            final MongoDatabase mongoDatabase = mongoClient.getDatabase(databaseName).withCodecRegistry(pojoCodecRegistry);
            MongoCollection collection = mongoDatabase.getCollection(getCollectionName(), getClazz());
            t.setDate(DateHandler.mongoDate());
            collection.insertOne(t);

        } finally {
            if (null != mongoClient) {
                mongoClient.close();
            }
        }
    }

上面的功能执行起来是没有问题的,同样的功能,迁移到springboot项目中,使用下列代码进行插入操作:

mongoTemplate.insert(t);

发现出现上面的错误。

解决

通过搜索,找打一些说法,

You'r using a primitive long which has an implicit, pre-assigned value. Hence, that value is passed to MongoDB and it persists it as is as it assumes you'd like to define identifiers manually.
The trick is to just use a wrapper type, as this can be null, MongoDB detects the absence of the value and will auto-populate an ObjectID for you. However, Long is not going to work as ObjectIDs do not fit into the number space of a Long. The id types supported for auto-generation are ObjectId, String and BigInteger.

大概意思是说实体中_id字段使用的是Long类型,spring-boot-data-mongo对于long类型,不支持自增运算,支持自增运算的类型包括BigInteger,String,ObjectId。
但是我的那个字段的类型是ObjectId的,显然不是由于上面原因导致的。但是如果Long类型定义id字段的小伙伴可以参考上面的做法,更改下类型。

我的插入集合中的实体定义的算是继承关系比较复杂,我的实体定义关系如下:

public class M(){
ObjectId _id;
}
public class B() extend M{
}
public class T() extend B{
}
T t = new T();
mongoTemplate.insert(t);

最后,把超类M中的ObjectId _id 那行代码去掉,让它自己去增加定义处理,发现问题解决。

相关文章

网友评论

      本文标题:E11000 duplicate key error colle

      本文链接:https://www.haomeiwen.com/subject/nwswpktx.html