Исключение при создании индекса - 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>