Исключение при создании индекса - Spring Data MongoDB

Я использую Spring-Data с Spring-Data MongoDB для отображения класса сущности с помощью аннотации @CompoundIndexes, в которой я указываю индексы как с именами, так и с их определениями. В своей производственной среде я решил, что мне нужно изменить некоторые атрибуты индекса на основе фактических данных. Теперь каждый раз, когда мое приложение запускается, оно не загружается, потому что не удалось создать индекс, соответствующий спецификации в результатах аннотации, и это исключение выдается в процессе инициализации (как показано ниже).

Есть ли способ настроить данные Spring и 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 ответ

Оказывается, это не так просто, как я думал, но это можно сделать с помощью нескольких дополнительных занятий.

Сначала вам нужно переопределить класс, который создает индексы, и переписать метод, который создает индексы, перехватывая соответствующие исключения и только регистрируя их.

К сожалению, это защищенный пакет, поэтому вам нужно будет создать класс в том же пакете, что и класс, который мы расширяем.

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;
        }

}

Обычно вы конфигурируете данные Spring mongoDB, используя класс, который расширяет AbstractMongoConfig и переопределяет метод "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>
Другие вопросы по тегам