创建索引时出现异常 - Spring Data MongoDB

时间:2016-11-12 23:16:17

标签: java spring mongodb spring-data-mongodb mongodb-indexes

我正在使用带有Spring-Data MongoDB的Spring-Data来使用@CompoundIndexes注释来映射实体类,其中我指定了具有名称及其定义的索引。在我的生产环境中,我决定需要根据实际数据更改索引的某些属性。现在,每次我的应用程序启动时,都无法加载,因为无法在注释结果中创建与规范匹配的索引,并且在初始化过程中抛出该异常(如下所示)。

是否有配置弹簧数据和mongodb以便记录这些异常,但不会导致容器启动失败?

Exception while creating index
! com.mongodb.MongoCommandException: Command failed with error 86: 'Trying to create an index with same name event_source_link_type_at_id_IDX with different key spec **** vs existing spec *****' on server 127.0.0.1:27017. The full response is { "ok" : 0.0, "errmsg" : "Trying to create an index with same name event_source_link_type_at_id_IDX with different key spec **** vs existing spec *****", "code" : 86 }
! at com.mongodb.connection.ProtocolHelper.getCommandFailureException(ProtocolHelper.java:115) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.connection.CommandProtocol.execute(CommandProtocol.java:114) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:159) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:286) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:173) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:215) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:198) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:170) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.operation.CreateIndexesOperation$1.call(CreateIndexesOperation.java:116) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.operation.CreateIndexesOperation$1.call(CreateIndexesOperation.java:111) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:230) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:221) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.operation.CreateIndexesOperation.execute(CreateIndexesOperation.java:111) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.operation.CreateIndexesOperation.execute(CreateIndexesOperation.java:66) ~[mongodb-driver-core-3.2.2.jar:na]
! at com.mongodb.Mongo.execute(Mongo.java:781) ~[mongodb-driver-3.2.2.jar:na]
! at com.mongodb.Mongo$2.execute(Mongo.java:764) ~[mongodb-driver-3.2.2.jar:na]
! at com.mongodb.DBCollection.createIndex(DBCollection.java:1541) ~[mongodb-driver-3.2.2.jar:na]
! at org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator.createIndex(MongoPersistentEntityIndexCreator.java:142) [spring-data-mongodb-1.8.4.RELEASE.jar:na]

1 个答案:

答案 0 :(得分:1)

事实证明这并不像我想象的那么容易,但可以通过一些额外的课程完成。

首先,您需要覆盖创建索引的类,并覆盖创建索引的方法,捕获匹配的异常并仅记录它们。

不幸的是它受到包保护,因此您需要在与我们扩展的类相同的包中创建一个类。

package org.springframework.data.mongodb.core.index;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;

public class ExceptionIgnoringIndexCreator extends MongoPersistentEntityIndexCreator {

    //assuming SLF4J as your logger otherwise, put your logger here
    private static final Logger LOG = LoggerFactory.getLogger(ExceptionIgnoringIndexCreator.class);

    public ExceptionIgnoringIndexCreator(MongoMappingContext mappingContext, MongoDbFactory mongoDbFactory) {
        super(mappingContext, mongoDbFactory);
    }

    @Override
    void createIndex(MongoPersistentEntityIndexResolver.IndexDefinitionHolder indexDefinition) {
        try {
            super.createIndex(indexDefinition);
        } catch (final RuntimeException exp) {
            final RuntimeException trans = translate(exp);
            if (trans != null) {
                throw trans;
            } else {
                LOG.warn("Exception while creating index", exp);
            }
        }
    }

    protected RuntimeException translate(final RuntimeException exp) {
        if (exp == null || exp.getMessage().contains("Cannot create index")) {
            return null;
        }
        return exp;
    }
}

索引创建由MongoMappingContext使用ApplicationEventPublisherInterface发布的事件触发。 我们需要一个类,我们可以懒惰地将另一个ApplicationEventPublisher设置为委托,接口的两个方法将委托他们的调用。

public class DelegatingPublisher implements ApplicationEventPublisher {

        private ApplicationEventPublisher delegate;

        @Override
        public void publishEvent(ApplicationEvent event) {
            delegate.publishEvent(event);
        }

        @Override
        public void publishEvent(Object event) {
            delegate.publishEvent(event);
        }

        public void setDelegate(ApplicationEventPublisher delegate) {
            this.delegate = delegate;
        }

}

通常使用扩展AbstractMongoConfig的类配置spring数据mongoDB并覆盖“mongo()”方法。

负责发布初始化索引的消息的组件是从“mongoMappingContext()”返回的消息,因此您需要覆盖该方法并扩展默认的MongoMappingContext,覆盖设置事件发布者并传递新的方法的方法。委托出版商代替。

@Configuration
@EnableMongoRepositories("com.my.company")
public class MyMongoConfig extends AbstractMongoConfiguration {
...
    @Override
    @Bean
    public MongoMappingContext mongoMappingContext() throws ClassNotFoundException {
        final DelegatingPublisher dep = new DelegatingPublisher();
        final MongoMappingContext mappingContext = new MongoMappingContext() {
            @Override
            public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
                super.setApplicationEventPublisher(dep);
            }
        };
        mappingContext.setInitialEntitySet(getInitialEntitySet());
        mappingContext.setSimpleTypeHolder(customConversions().getSimpleTypeHolder());
        mappingContext.setFieldNamingStrategy(fieldNamingStrategy());

        try {
            final MongoPersistentEntityIndexCreator indexCreator = new ExceptionIgnoringIndexCreator(mappingContext, mongoDbFactory());
            dep.setDelegate(new MongoMappingEventPublisher(indexCreator));
            return mappingContext;
        } catch (Exception exp) {
            throw new RuntimeException(exp);
        }
    }
...
}

如果使用基于XML的配置,则还需要一个类和指定的配置

public class EventDelegatingMongoMappingContext extends MongoMappingContext {

    private ApplicationEventPublisher publisher;

    public EventDelegatingMongoMappingContext(ApplicationEventPublisher publisher) {
        this.publisher = publisher;    
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        super.setApplicationEventPublisher(publisher);
    }
}
<mongo:db-factory id="mongoDbFactory"
                  host="localhost"
                  port="27017"
                  dbname="database"
                  username="mycompany"
                  password="secret"/>

<bean id="delegatingPublisher" class="com.my.company.DelegatingPublisher">
    <property name="delegate" ref="mappingEventPublisher" />
</bean>

<!-- Must be named 'mongoMappingContext' to be recognized up -->
<bean id="mongoMappingContext" class="com.my.company.EventDelegatingMongoMappingContext">
    <constructor-arg>
        <bean ref="delegatingPublisher" />
    </constructor-arg>
</bean>

<bean id="mongoIndexCreator" class="org.springframework.data.mongodb.core.index.ExceptionIgnoringIndexCreator">
    <constructor-arg>
        <bean ref="mongoMappingContext"/>
    </constructor-arg>
    <constructor-arg>
        <bean ref="mongoDbFactory"/>
    </constructor-arg>
</bean>

<bean id="mappingEventPublisher" class="org.springframework.data.mongodb.core.index.MongoMappingEventPublisher">
    <constructor-arg>
        <bean ref="mongoIndexCreator"/>
    </constructor-arg>
</bean>