DATAMONGO-2188 - Deprecate auto-index creation & introduce configuration to disable it.

Auto-index creation can now be disabled by setting MongoMappingContext.setAutoIndexCreation(false). This configuration prevents automatic index creation on application startup and during access to entities.

Original Pull Request: #636
This commit is contained in:
Mark Paluch
2019-01-16 14:04:12 +01:00
committed by Christoph Strobl
parent 8f3dacd55e
commit 33fa79b29f
13 changed files with 129 additions and 48 deletions

View File

@@ -246,10 +246,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
mappingContext = this.mongoConverter.getMappingContext();
// We create indexes based on mapping events
if (mappingContext instanceof MongoMappingContext) {
indexCreator = new MongoPersistentEntityIndexCreator((MongoMappingContext) mappingContext, this);
eventPublisher = new MongoMappingEventPublisher(indexCreator);
if (mappingContext instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) mappingContext).setApplicationEventPublisher(eventPublisher);
MongoMappingContext mappingContext = (MongoMappingContext) this.mappingContext;
if (mappingContext.isAutoIndexCreation()) {
indexCreator = new MongoPersistentEntityIndexCreator(mappingContext, this);
eventPublisher = new MongoMappingEventPublisher(indexCreator);
mappingContext.setApplicationEventPublisher(eventPublisher);
}
}
}
@@ -1582,7 +1586,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
query.getCollation().map(Collation::toMongoCollation).ifPresent(opts::collation);
}
Document updateObj = update instanceof MappedUpdate ? update.getUpdateObject() : updateMapper.getMappedObject(update.getUpdateObject(), entity);
Document updateObj = update instanceof MappedUpdate ? update.getUpdateObject()
: updateMapper.getMappedObject(update.getUpdateObject(), entity);
if (multi && update.isIsolated() && !queryObj.containsKey("$isolated")) {
queryObj.put("$isolated", 1);
@@ -1617,7 +1622,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
});
}
private void increaseVersionForUpdateIfNecessary(@Nullable MongoPersistentEntity<?> persistentEntity, UpdateDefinition update) {
private void increaseVersionForUpdateIfNecessary(@Nullable MongoPersistentEntity<?> persistentEntity,
UpdateDefinition update) {
if (persistentEntity != null && persistentEntity.hasVersionProperty()) {
String versionFieldName = persistentEntity.getRequiredVersionProperty().getFieldName();

View File

@@ -232,12 +232,15 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
if (this.mappingContext instanceof MongoMappingContext) {
MongoMappingContext mongoMappingContext = (MongoMappingContext) this.mappingContext;
this.indexCreator = new ReactiveMongoPersistentEntityIndexCreator(mongoMappingContext, this::indexOps);
this.eventPublisher = new MongoMappingEventPublisher(this.indexCreatorListener);
mongoMappingContext.setApplicationEventPublisher(this.eventPublisher);
this.mappingContext.getPersistentEntities()
.forEach(entity -> onCheckForIndexes(entity, subscriptionExceptionHandler));
if (mongoMappingContext.isAutoIndexCreation()) {
this.indexCreator = new ReactiveMongoPersistentEntityIndexCreator(mongoMappingContext, this::indexOps);
this.eventPublisher = new MongoMappingEventPublisher(this.indexCreatorListener);
mongoMappingContext.setApplicationEventPublisher(this.eventPublisher);
this.mappingContext.getPersistentEntities()
.forEach(entity -> onCheckForIndexes(entity, subscriptionExceptionHandler));
}
}
}

View File

@@ -55,7 +55,8 @@ public @interface CompoundIndex {
/**
* @return
* @see <a href="https://docs.mongodb.org/manual/core/index-unique/">https://docs.mongodb.org/manual/core/index-unique/</a>
* @see <a href=
* "https://docs.mongodb.org/manual/core/index-unique/">https://docs.mongodb.org/manual/core/index-unique/</a>
*/
boolean unique() default false;
@@ -63,13 +64,15 @@ public @interface CompoundIndex {
* If set to true index will skip over any document that is missing the indexed field.
*
* @return
* @see <a href="https://docs.mongodb.org/manual/core/index-sparse/">https://docs.mongodb.org/manual/core/index-sparse/</a>
* @see <a href=
* "https://docs.mongodb.org/manual/core/index-sparse/">https://docs.mongodb.org/manual/core/index-sparse/</a>
*/
boolean sparse() default false;
/**
* @return
* @see <a href="https://docs.mongodb.org/manual/core/index-creation/#index-creation-duplicate-dropping">https://docs.mongodb.org/manual/core/index-creation/#index-creation-duplicate-dropping</a>
* @see <a href=
* "https://docs.mongodb.org/manual/core/index-creation/#index-creation-duplicate-dropping">https://docs.mongodb.org/manual/core/index-creation/#index-creation-duplicate-dropping</a>
* @deprecated since 2.1. No longer supported by MongoDB as of server version 3.0.
*/
@Deprecated
@@ -131,7 +134,8 @@ public @interface CompoundIndex {
* If {@literal true} the index will be created in the background.
*
* @return
* @see <a href="https://docs.mongodb.org/manual/core/indexes/#background-construction">https://docs.mongodb.org/manual/core/indexes/#background-construction</a>
* @see <a href=
* "https://docs.mongodb.org/manual/core/indexes/#background-construction">https://docs.mongodb.org/manual/core/indexes/#background-construction</a>
*/
boolean background() default false;

View File

@@ -28,8 +28,8 @@ import org.springframework.data.util.TypeInformation;
interface IndexResolver {
/**
* Find and create {@link IndexDefinition}s for properties of given {@link TypeInformation}. {@link IndexDefinition}s are created
* for properties and types with {@link Indexed}, {@link CompoundIndexes} or {@link GeoSpatialIndexed}.
* Find and create {@link IndexDefinition}s for properties of given {@link TypeInformation}. {@link IndexDefinition}s
* are created for properties and types with {@link Indexed}, {@link CompoundIndexes} or {@link GeoSpatialIndexed}.
*
* @param typeInformation
* @return Empty {@link Iterable} in case no {@link IndexDefinition} could be resolved for type.

View File

@@ -31,7 +31,7 @@ import java.lang.annotation.Target;
* @author Christoph Strobl
* @author Jordi Llach
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD})
@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Indexed {
@@ -39,7 +39,8 @@ public @interface Indexed {
* If set to true reject all documents that contain a duplicate value for the indexed field.
*
* @return
* @see <a href="https://docs.mongodb.org/manual/core/index-unique/">https://docs.mongodb.org/manual/core/index-unique/</a>
* @see <a href=
* "https://docs.mongodb.org/manual/core/index-unique/">https://docs.mongodb.org/manual/core/index-unique/</a>
*/
boolean unique() default false;
@@ -49,13 +50,15 @@ public @interface Indexed {
* If set to true index will skip over any document that is missing the indexed field.
*
* @return
* @see <a href="https://docs.mongodb.org/manual/core/index-sparse/">https://docs.mongodb.org/manual/core/index-sparse/</a>
* @see <a href=
* "https://docs.mongodb.org/manual/core/index-sparse/">https://docs.mongodb.org/manual/core/index-sparse/</a>
*/
boolean sparse() default false;
/**
* @return
* @see <a href="https://docs.mongodb.org/manual/core/index-creation/#index-creation-duplicate-dropping">https://docs.mongodb.org/manual/core/index-creation/#index-creation-duplicate-dropping</a>
* @see <a href=
* "https://docs.mongodb.org/manual/core/index-creation/#index-creation-duplicate-dropping">https://docs.mongodb.org/manual/core/index-creation/#index-creation-duplicate-dropping</a>
* @deprecated since 2.1. No longer supported by MongoDB as of server version 3.0.
*/
@Deprecated
@@ -115,7 +118,8 @@ public @interface Indexed {
* If {@literal true} the index will be created in the background.
*
* @return
* @see <a href="https://docs.mongodb.org/manual/core/indexes/#background-construction">https://docs.mongodb.org/manual/core/indexes/#background-construction</a>
* @see <a href=
* "https://docs.mongodb.org/manual/core/indexes/#background-construction">https://docs.mongodb.org/manual/core/indexes/#background-construction</a>
*/
boolean background() default false;
@@ -123,7 +127,8 @@ public @interface Indexed {
* Configures the number of seconds after which the collection should expire. Defaults to -1 for no expiry.
*
* @return
* @see <a href="https://docs.mongodb.org/manual/tutorial/expire-data/">https://docs.mongodb.org/manual/tutorial/expire-data/</a>
* @see <a href=
* "https://docs.mongodb.org/manual/tutorial/expire-data/">https://docs.mongodb.org/manual/tutorial/expire-data/</a>
*/
int expireAfterSeconds() default -1;
}

View File

@@ -36,7 +36,9 @@ import org.springframework.util.Assert;
* @author Jon Brisbin
* @author Oliver Gierke
* @author Mark Paluch
* @deprecated since 2.2. Use {@link IndexOperations} to define and create indexes.
*/
@Deprecated
public class MongoMappingEventPublisher implements ApplicationEventPublisher {
private final ApplicationListener<MappingContextEvent<?, ?>> indexCreator;

View File

@@ -63,10 +63,12 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener<Ma
/**
* Creates a new {@link MongoPersistentEntityIndexCreator} for the given {@link MongoMappingContext} and
* {@link MongoDbFactory}.
* @param mappingContext must not be {@literal null}.
*
* @param mappingContext must not be {@literal null}.
* @param indexOperationsProvider must not be {@literal null}.
*/
public MongoPersistentEntityIndexCreator(MongoMappingContext mappingContext, IndexOperationsProvider indexOperationsProvider) {
public MongoPersistentEntityIndexCreator(MongoMappingContext mappingContext,
IndexOperationsProvider indexOperationsProvider) {
this(mappingContext, indexOperationsProvider, new MongoPersistentEntityIndexResolver(mappingContext));
}
@@ -78,8 +80,8 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener<Ma
* @param mongoDbFactory must not be {@literal null}.
* @param indexResolver must not be {@literal null}.
*/
public MongoPersistentEntityIndexCreator(MongoMappingContext mappingContext, IndexOperationsProvider indexOperationsProvider,
IndexResolver indexResolver) {
public MongoPersistentEntityIndexCreator(MongoMappingContext mappingContext,
IndexOperationsProvider indexOperationsProvider, IndexResolver indexResolver) {
Assert.notNull(mappingContext, "MongoMappingContext must not be null!");
Assert.notNull(indexOperationsProvider, "IndexOperationsProvider must not be null!");
@@ -146,8 +148,8 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener<Ma
} catch (UncategorizedMongoDbException ex) {
if (ex.getCause() instanceof MongoException &&
MongoDbErrorCodes.isDataIntegrityViolationCode(((MongoException) ex.getCause()).getCode())) {
if (ex.getCause() instanceof MongoException
&& MongoDbErrorCodes.isDataIntegrityViolationCode(((MongoException) ex.getCause()).getCode())) {
IndexInfo existingIndex = fetchIndexInformation(indexDefinition);
String message = "Cannot create index for '%s' in collection '%s' with keys '%s' and options '%s'.";

View File

@@ -43,6 +43,7 @@ public class MongoMappingContext extends AbstractMappingContext<BasicMongoPersis
private FieldNamingStrategy fieldNamingStrategy = DEFAULT_NAMING_STRATEGY;
private @Nullable ApplicationContext context;
private boolean autoIndexCreation = true;
/**
* Creates a new {@link MongoMappingContext}.
@@ -101,4 +102,28 @@ public class MongoMappingContext extends AbstractMappingContext<BasicMongoPersis
this.context = applicationContext;
}
/**
* Returns whether auto-index creation is enabled or disabled. Please note that auto-index creation is deprecated.
* Index creation should happen at a well-defined time that is ideally controlled by the application itself.
*
* @return {@literal true} when auto-index creation is enabled; {@literal false} otherwise.
* @since 2.2
* @see org.springframework.data.mongodb.core.index.Indexed
*/
public boolean isAutoIndexCreation() {
return autoIndexCreation;
}
/**
* Enables/disables auto-index creation. Please note that auto-index creation is deprecated. Index creation should
* happen at a well-defined time that is ideally controlled by the application its
*
* @param autoCreateIndexes {@literal true} to enable auto-index creation. Enabled by default.
* @since 2.2
* @see org.springframework.data.mongodb.core.index.Indexed
*/
public void setAutoIndexCreation(boolean autoCreateIndexes) {
this.autoIndexCreation = autoCreateIndexes;
}
}

View File

@@ -42,7 +42,10 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @author Mark Paluch
* @author Christoph Strobl
* @deprecated since 2.2. Use {@link org.springframework.data.mongodb.core.index.IndexOperations} to define and create
* indexes.
*/
@Deprecated
class IndexEnsuringQueryCreationListener implements QueryCreationListener<PartTreeMongoQuery> {
private static final Set<Type> GEOSPATIAL_TYPES = new HashSet<Type>(Arrays.asList(Type.NEAR, Type.WITHIN));

View File

@@ -35,7 +35,7 @@ public class MongoRepositoryFactoryBean<T extends Repository<S, ID>, S, ID exten
extends RepositoryFactoryBeanSupport<T, S, ID> {
private @Nullable MongoOperations operations;
private boolean createIndexesForQueryMethods = false;
@Deprecated private boolean createIndexesForQueryMethods = false;
private boolean mappingContextConfigured = false;
/**
@@ -60,7 +60,10 @@ public class MongoRepositoryFactoryBean<T extends Repository<S, ID>, S, ID exten
* Configures whether to automatically create indexes for the properties referenced in a query method.
*
* @param createIndexesForQueryMethods the createIndexesForQueryMethods to set
* @deprecated since 2.2. Use {@link org.springframework.data.mongodb.core.index.IndexOperations} to define and create
* indexes.
*/
@Deprecated
public void setCreateIndexesForQueryMethods(boolean createIndexesForQueryMethods) {
this.createIndexesForQueryMethods = createIndexesForQueryMethods;
}

View File

@@ -40,7 +40,8 @@ public class ReactiveMongoRepositoryFactoryBean<T extends Repository<S, ID>, S,
extends RepositoryFactoryBeanSupport<T, S, ID> {
private @Nullable ReactiveMongoOperations operations;
private boolean createIndexesForQueryMethods = false;
@Deprecated private boolean createIndexesForQueryMethods = false;
private boolean mappingContextConfigured = false;
/**
@@ -65,7 +66,10 @@ public class ReactiveMongoRepositoryFactoryBean<T extends Repository<S, ID>, S,
* Configures whether to automatically create indexes for the properties referenced in a query method.
*
* @param createIndexesForQueryMethods the createIndexesForQueryMethods to set
* @deprecated since 2.2. Use {@link org.springframework.data.mongodb.core.index.IndexOperations} to define and create
* indexes.
*/
@Deprecated
public void setCreateIndexesForQueryMethods(boolean createIndexesForQueryMethods) {
this.createIndexesForQueryMethods = createIndexesForQueryMethods;
}

View File

@@ -30,25 +30,26 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.MongoCollectionUtils;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.CollectionCallback;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mongodb.MongoException;
import com.mongodb.client.MongoCollection;
/**
* Integration tests for index handling.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Jordi Llach
* @author Mark Paluch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml")
@@ -72,6 +73,21 @@ public class IndexingIntegrationTests {
assertThat(hasIndex("_firstname", IndexedPerson.class), is(true));
}
@Test // DATAMONGO-237
@DirtiesContext
public void shouldNotCreateIndexOnIndexingDisabled() {
MongoMappingContext context = new MongoMappingContext();
context.setAutoIndexCreation(false);
MongoTemplate template = new MongoTemplate(mongoDbFactory,
new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, context));
template.getConverter().getMappingContext().getPersistentEntity(IndexedPerson.class);
assertThat(hasIndex("_firstname", MongoCollectionUtils.getPreferredCollectionName(IndexedPerson.class)), is(false));
}
@Test // DATAMONGO-1163
@DirtiesContext
public void createsIndexFromMetaAnnotation() {
@@ -101,22 +117,30 @@ public class IndexingIntegrationTests {
* @param entityType
* @return
*/
private boolean hasIndex(final String indexName, Class<?> entityType) {
private boolean hasIndex(String indexName, Class<?> entityType) {
return hasIndex(indexName, operations.getCollectionName(entityType));
}
return operations.execute(entityType, new CollectionCallback<Boolean>() {
public Boolean doInCollection(MongoCollection<org.bson.Document> collection)
throws MongoException, DataAccessException {
/**
* Returns whether an index with the given name exists for the given collection.
*
* @param indexName
* @param collectionName
* @return
*/
private boolean hasIndex(String indexName, String collectionName) {
List<org.bson.Document> indexes = new ArrayList<org.bson.Document>();
collection.listIndexes(org.bson.Document.class).into(indexes);
return operations.execute(collectionName, collection -> {
for (org.bson.Document indexInfo : indexes) {
if (indexName.equals(indexInfo.get("name"))) {
return true;
}
List<org.bson.Document> indexes = new ArrayList<>();
collection.listIndexes(org.bson.Document.class).into(indexes);
for (org.bson.Document indexInfo : indexes) {
if (indexName.equals(indexInfo.get("name"))) {
return true;
}
return false;
}
return false;
});
}
}