美文网首页
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