DATAMONGO-500 - Index creation is only done for the correct MappingContext.
Index creation now double checks the MappingContext a MappingContextEvent originates from before actually creating indexes. This avoids invalid indexes being created in a multi-database scenario.
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
<org.springframework.version.30>3.0.7.RELEASE</org.springframework.version.30>
|
||||
<org.springframework.version.40>4.0.0.RELEASE</org.springframework.version.40>
|
||||
<org.springframework.version.range>[${org.springframework.version.30}, ${org.springframework.version.40})</org.springframework.version.range>
|
||||
<data.commons.version>1.4.0.M1</data.commons.version>
|
||||
<data.commons.version>1.4.0.BUILD-SNAPSHOT</data.commons.version>
|
||||
<aspectj.version>1.6.11.RELEASE</aspectj.version>
|
||||
<bundlor.failOnWarnings>true</bundlor.failOnWarnings>
|
||||
</properties>
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
import org.springframework.data.config.BeanComponentDefinitionBuilder;
|
||||
import org.springframework.data.mongodb.core.convert.CustomConversions;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
|
||||
@@ -166,27 +167,35 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser {
|
||||
BeanDefinition conversionsDefinition) {
|
||||
|
||||
String ctxRef = element.getAttribute("mapping-context-ref");
|
||||
if (!StringUtils.hasText(ctxRef)) {
|
||||
BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(MongoMappingContext.class);
|
||||
|
||||
Set<String> classesToAdd = getInititalEntityClasses(element, mappingContextBuilder);
|
||||
if (classesToAdd != null) {
|
||||
mappingContextBuilder.addPropertyValue("initialEntitySet", classesToAdd);
|
||||
}
|
||||
|
||||
if (conversionsDefinition != null) {
|
||||
AbstractBeanDefinition simpleTypesDefinition = new GenericBeanDefinition();
|
||||
simpleTypesDefinition.setFactoryBeanName("customConversions");
|
||||
simpleTypesDefinition.setFactoryMethodName("getSimpleTypeHolder");
|
||||
|
||||
mappingContextBuilder.addPropertyValue("simpleTypeHolder", simpleTypesDefinition);
|
||||
}
|
||||
|
||||
parserContext.getRegistry().registerBeanDefinition(MAPPING_CONTEXT, mappingContextBuilder.getBeanDefinition());
|
||||
ctxRef = MAPPING_CONTEXT;
|
||||
if (StringUtils.hasText(ctxRef)) {
|
||||
return ctxRef;
|
||||
}
|
||||
|
||||
BeanComponentDefinitionBuilder componentDefinitionBuilder = new BeanComponentDefinitionBuilder(element,
|
||||
parserContext);
|
||||
|
||||
BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(MongoMappingContext.class);
|
||||
|
||||
Set<String> classesToAdd = getInititalEntityClasses(element, mappingContextBuilder);
|
||||
if (classesToAdd != null) {
|
||||
mappingContextBuilder.addPropertyValue("initialEntitySet", classesToAdd);
|
||||
}
|
||||
|
||||
if (conversionsDefinition != null) {
|
||||
AbstractBeanDefinition simpleTypesDefinition = new GenericBeanDefinition();
|
||||
simpleTypesDefinition.setFactoryBeanName("customConversions");
|
||||
simpleTypesDefinition.setFactoryMethodName("getSimpleTypeHolder");
|
||||
|
||||
mappingContextBuilder.addPropertyValue("simpleTypeHolder", simpleTypesDefinition);
|
||||
}
|
||||
|
||||
parserContext.getRegistry().registerBeanDefinition(MAPPING_CONTEXT, mappingContextBuilder.getBeanDefinition());
|
||||
ctxRef = MAPPING_CONTEXT;
|
||||
|
||||
parserContext.registerBeanComponent(componentDefinitionBuilder.getComponent(mappingContextBuilder, ctxRef));
|
||||
|
||||
return ctxRef;
|
||||
}
|
||||
|
||||
|
||||
@@ -1356,21 +1356,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
ConversionService conversionService = mongoConverter.getConversionService();
|
||||
BeanWrapper<PersistentEntity<Object, ?>, Object> wrapper = BeanWrapper.create(savedObject, conversionService);
|
||||
|
||||
try {
|
||||
Object idValue = wrapper.getProperty(idProp, idProp.getType(), true);
|
||||
|
||||
Object idValue = wrapper.getProperty(idProp, idProp.getType(), true);
|
||||
|
||||
if (idValue != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
wrapper.setProperty(idProp, id);
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
if (idValue != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
wrapper.setProperty(idProp, id);
|
||||
}
|
||||
|
||||
private DBCollection getAndPrepareCollection(DB db, String collectionName) {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -253,13 +252,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
public void doWithAssociation(Association<MongoPersistentProperty> association) {
|
||||
MongoPersistentProperty inverseProp = association.getInverse();
|
||||
Object obj = getValueInternal(inverseProp, dbo, evaluator, result);
|
||||
try {
|
||||
wrapper.setProperty(inverseProp, obj);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
wrapper.setProperty(inverseProp, obj);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -13,37 +13,51 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.mapping.event.MappingContextEvent;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent;
|
||||
import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An implementation of ApplicationEventPublisher that will only fire MappingContextEvents for use by the index creator
|
||||
* when MongoTemplate is used 'stand-alone', that is not declared inside a Spring ApplicationContext.
|
||||
* An implementation of ApplicationEventPublisher that will only fire {@link MappingContextEvent}s for use by the index
|
||||
* creator when MongoTemplate is used 'stand-alone', that is not declared inside a Spring {@link ApplicationContext}.
|
||||
* Declare {@link MongoTemplate} inside an {@link ApplicationContext} to enable the publishing of all persistence events
|
||||
* such as {@link AfterLoadEvent}, {@link AfterSaveEvent}, etc.
|
||||
*
|
||||
* Declare MongoTemplate inside an ApplicationContext to enable the publishing of all persistence events such as
|
||||
* {@link AfterLoadEvent}, {@link AfterSaveEvent}, etc.
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MongoMappingEventPublisher implements ApplicationEventPublisher {
|
||||
|
||||
private MongoPersistentEntityIndexCreator indexCreator;
|
||||
private final MongoPersistentEntityIndexCreator indexCreator;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MongoMappingEventPublisher} for the given {@link MongoPersistentEntityIndexCreator}.
|
||||
*
|
||||
* @param indexCreator must not be {@literal null}.
|
||||
*/
|
||||
public MongoMappingEventPublisher(MongoPersistentEntityIndexCreator indexCreator) {
|
||||
|
||||
Assert.notNull(indexCreator);
|
||||
this.indexCreator = indexCreator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationEventPublisher#publishEvent(org.springframework.context.ApplicationEvent)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void publishEvent(ApplicationEvent event) {
|
||||
if (event instanceof MappingContextEvent) {
|
||||
indexCreator
|
||||
.onApplicationEvent((MappingContextEvent<MongoPersistentEntity<MongoPersistentProperty>, MongoPersistentProperty>) event);
|
||||
indexCreator.onApplicationEvent((MappingContextEvent<MongoPersistentEntity<?>, MongoPersistentProperty>) event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +44,13 @@ import com.mongodb.util.JSON;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MongoPersistentEntityIndexCreator implements
|
||||
ApplicationListener<MappingContextEvent<MongoPersistentEntity<MongoPersistentProperty>, MongoPersistentProperty>> {
|
||||
ApplicationListener<MappingContextEvent<MongoPersistentEntity<?>, MongoPersistentProperty>> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MongoPersistentEntityIndexCreator.class);
|
||||
|
||||
private final Map<Class<?>, Boolean> classesSeen = new ConcurrentHashMap<Class<?>, Boolean>();
|
||||
private final MongoDbFactory mongoDbFactory;
|
||||
private final MongoMappingContext mappingContext;
|
||||
|
||||
/**
|
||||
* Creats a new {@link MongoPersistentEntityIndexCreator} for the given {@link MongoMappingContext} and
|
||||
@@ -62,7 +63,9 @@ public class MongoPersistentEntityIndexCreator implements
|
||||
|
||||
Assert.notNull(mongoDbFactory);
|
||||
Assert.notNull(mappingContext);
|
||||
|
||||
this.mongoDbFactory = mongoDbFactory;
|
||||
this.mappingContext = mappingContext;
|
||||
|
||||
for (MongoPersistentEntity<?> entity : mappingContext.getPersistentEntities()) {
|
||||
checkForIndexes(entity);
|
||||
@@ -73,8 +76,11 @@ public class MongoPersistentEntityIndexCreator implements
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
|
||||
*/
|
||||
public void onApplicationEvent(
|
||||
MappingContextEvent<MongoPersistentEntity<MongoPersistentProperty>, MongoPersistentProperty> event) {
|
||||
public void onApplicationEvent(MappingContextEvent<MongoPersistentEntity<?>, MongoPersistentProperty> event) {
|
||||
|
||||
if (!event.wasEmittedBy(mappingContext)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PersistentEntity<?, ?> entity = event.getPersistentEntity();
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link MongoPersistentEntityIndexCreator}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class MongoPersistentEntityIndexCreatorIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("mongo1")
|
||||
MongoOperations templateOne;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("mongo2")
|
||||
MongoOperations templateTwo;
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
templateOne.dropCollection(SampleEntity.class);
|
||||
templateTwo.dropCollection(SampleEntity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void foo() {
|
||||
|
||||
List<IndexInfo> indexInfo = templateOne.indexOps(SampleEntity.class).getIndexInfo();
|
||||
assertThat(indexInfo, hasSize(greaterThan(0)));
|
||||
assertThat(indexInfo, Matchers.<IndexInfo> hasItem(hasProperty("name", is("prop"))));
|
||||
|
||||
indexInfo = templateTwo.indexOps(SampleEntity.class).getIndexInfo();
|
||||
assertThat(indexInfo, hasSize(0));
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,13 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.mapping.event.MappingContextEvent;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
@@ -40,6 +44,8 @@ public class MongoPersistentEntityIndexCreatorUnitTests {
|
||||
|
||||
@Mock
|
||||
MongoDbFactory factory;
|
||||
@Mock
|
||||
ApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void buildsIndexDefinitionUsingFieldName() {
|
||||
@@ -55,6 +61,26 @@ public class MongoPersistentEntityIndexCreatorUnitTests {
|
||||
assertThat(creator.name, is("indexName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotCreateIndexForEntityComingFromDifferentMappingContext() {
|
||||
|
||||
MongoMappingContext mappingContext = new MongoMappingContext();
|
||||
|
||||
MongoMappingContext personMappingContext = new MongoMappingContext();
|
||||
personMappingContext.setInitialEntitySet(Collections.singleton(Person.class));
|
||||
personMappingContext.initialize();
|
||||
|
||||
DummyMongoPersistentEntityIndexCreator creator = new DummyMongoPersistentEntityIndexCreator(mappingContext, factory);
|
||||
|
||||
MongoPersistentEntity<?> entity = personMappingContext.getPersistentEntity(Person.class);
|
||||
MappingContextEvent<MongoPersistentEntity<?>, MongoPersistentProperty> event = new MappingContextEvent<MongoPersistentEntity<?>, MongoPersistentProperty>(
|
||||
personMappingContext, entity);
|
||||
|
||||
creator.onApplicationEvent(event);
|
||||
|
||||
assertThat(creator.indexDefinition, is(nullValue()));
|
||||
}
|
||||
|
||||
static class Person {
|
||||
|
||||
@Indexed(name = "indexName")
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
@Document
|
||||
public class SampleEntity {
|
||||
|
||||
@Id
|
||||
String id;
|
||||
|
||||
@Indexed
|
||||
String prop;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<mongo:mapping-converter id="mongoConverter" db-factory-ref="factory1"
|
||||
base-package="org.springframework.data.mongodb.core.index" />
|
||||
|
||||
<bean id="mongo1" class="org.springframework.data.mongodb.core.MongoTemplate">
|
||||
<constructor-arg ref="factory1" />
|
||||
<constructor-arg ref="mongoConverter" />
|
||||
</bean>
|
||||
|
||||
<bean id="mongo2" class="org.springframework.data.mongodb.core.MongoTemplate">
|
||||
<constructor-arg ref="factory2" />
|
||||
</bean>
|
||||
|
||||
<mongo:db-factory id="factory1" host="127.0.0.1" dbname="mongo-index-db1" />
|
||||
<mongo:db-factory id="factory2" host="127.0.0.1" dbname="mongo-index-db2" />
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user