Fixes for handling Arrays, had to change index creation from event-based to synchronous for simplicity's sake.

This commit is contained in:
Jon Brisbin
2011-03-21 11:41:08 -05:00
committed by J. Brisbin
parent d01867b072
commit a1ef9abef2
10 changed files with 269 additions and 138 deletions

View File

@@ -26,7 +26,6 @@ import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
@@ -35,8 +34,8 @@ import org.springframework.data.annotation.Persistent;
import org.springframework.data.document.mongodb.convert.MappingMongoConverter;
import org.springframework.data.document.mongodb.mapping.Document;
import org.springframework.data.document.mongodb.mapping.MongoMappingConfigurationBuilder;
import org.springframework.data.document.mongodb.mapping.event.IndexCreationListener;
import org.springframework.data.mapping.BasicMappingContext;
import org.springframework.data.document.mongodb.mapping.MongoMappingContext;
import org.springframework.data.document.mongodb.mapping.index.IndexCreationHelper;
import org.springframework.data.mapping.model.MappingException;
import org.w3c.dom.Element;
@@ -51,7 +50,7 @@ public class MongoMappingConverterParser extends AbstractBeanDefinitionParser {
private static final String CONFIGURATION_BUILDER = "mappingConfigurationBuilder";
private static final String MAPPING_CONTEXT = "mappingContext";
private static final String INDEX_CREATION_LISTENER = "indexCreationListener";
private static final String INDEX_CREATION_HELPER = "indexCreationHelper";
private static final String TEMPLATE = "mongoTemplate";
private static final String BASE_PACKAGE = "base-package";
@@ -66,16 +65,16 @@ public class MongoMappingConverterParser extends AbstractBeanDefinitionParser {
String builderRef = element.getAttribute("mapping-config-builder-ref");
if (null == builderRef || "".equals(builderRef)) {
GenericBeanDefinition builder = new GenericBeanDefinition();
builder.setBeanClass(MongoMappingConfigurationBuilder.class);
registry.registerBeanDefinition(CONFIGURATION_BUILDER, builder);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MongoMappingConfigurationBuilder.class);
registry.registerBeanDefinition(CONFIGURATION_BUILDER, builder.getBeanDefinition());
builderRef = CONFIGURATION_BUILDER;
}
String ctxRef = element.getAttribute("mapping-context-ref");
if (null == ctxRef || "".equals(ctxRef)) {
BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder.genericBeanDefinition(BasicMappingContext.class);
BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoMappingContext.class);
mappingContextBuilder.addPropertyReference("mappingConfigurationBuilder", builderRef);
//mappingContextBuilder.addPropertyValue("indexCreationHelper", new RuntimeBeanReference(INDEX_CREATION_HELPER));
registry.registerBeanDefinition(MAPPING_CONTEXT, mappingContextBuilder.getBeanDefinition());
ctxRef = MAPPING_CONTEXT;
}
@@ -95,18 +94,19 @@ public class MongoMappingConverterParser extends AbstractBeanDefinitionParser {
}
converterBuilder.addPropertyReference("mongo", mongoRef);
/*
try {
registry.getBeanDefinition(INDEX_CREATION_LISTENER);
registry.getBeanDefinition(INDEX_CREATION_HELPER);
} catch (NoSuchBeanDefinitionException ignored) {
String templateRef = element.getAttribute("mongo-template-ref");
if (null == templateRef || "".equals(templateRef)) {
templateRef = TEMPLATE;
}
BeanDefinitionBuilder indexListenerBuilder = BeanDefinitionBuilder.genericBeanDefinition(IndexCreationListener.class);
BeanDefinitionBuilder indexListenerBuilder = BeanDefinitionBuilder.genericBeanDefinition(IndexCreationHelper.class);
indexListenerBuilder.addPropertyValue("mongoTemplate", new RuntimeBeanReference(templateRef));
indexListenerBuilder.setDestroyMethodName("cleanUp");
registry.registerBeanDefinition(INDEX_CREATION_LISTENER, indexListenerBuilder.getBeanDefinition());
registry.registerBeanDefinition(INDEX_CREATION_HELPER, indexListenerBuilder.getBeanDefinition());
}
*/
// Scan for @Document entities
String basePackage = element.getAttribute(BASE_PACKAGE);

View File

@@ -434,6 +434,8 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
if (null != dbref) {
DBRef dbRef = createDBRef(propObjItem, dbref);
dbList.add(dbRef);
} else if (MappingBeanHelper.isSimpleType(type.getComponentType())) {
dbList.add(propObjItem);
} else {
BasicDBObject propDbObj = new BasicDBObject();
write(propObjItem, propDbObj, mappingContext.getPersistentEntity(prop.getTypeInformation()));

View File

@@ -21,9 +21,12 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.Persistent;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@Persistent
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.TYPE,

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* 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.document.mongodb.mapping;
import org.springframework.data.document.mongodb.mapping.index.IndexCreationHelper;
import org.springframework.data.mapping.BasicMappingContext;
import org.springframework.data.mapping.model.PersistentEntity;
import org.springframework.data.util.TypeInformation;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MongoMappingContext extends BasicMappingContext {
protected IndexCreationHelper indexCreationHelper;
public MongoMappingContext() {
builder = new MongoMappingConfigurationBuilder();
}
public IndexCreationHelper getIndexCreationHelper() {
return indexCreationHelper;
}
public void setIndexCreationHelper(IndexCreationHelper indexCreationHelper) {
this.indexCreationHelper = indexCreationHelper;
}
@Override
public <T> PersistentEntity<T> addPersistentEntity(TypeInformation typeInformation) {
PersistentEntity<T> entity = super.addPersistentEntity(typeInformation);
if (entity instanceof MongoPersistentEntity && null != indexCreationHelper) {
indexCreationHelper.checkForIndexes((MongoPersistentEntity<?>) entity);
}
return entity;
}
}

View File

@@ -16,39 +16,20 @@
package org.springframework.data.document.mongodb.mapping.event;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
import com.mongodb.util.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.CollectionCallback;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.index.CompoundIndex;
import org.springframework.data.document.mongodb.index.CompoundIndexes;
import org.springframework.data.document.mongodb.index.IndexDirection;
import org.springframework.data.document.mongodb.index.Indexed;
import org.springframework.data.document.mongodb.mapping.Document;
import org.springframework.data.document.mongodb.mapping.MongoPersistentEntity;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.document.mongodb.mapping.index.IndexCreationHelper;
import org.springframework.data.mapping.event.MappingContextEvent;
import org.springframework.data.mapping.model.PersistentProperty;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
@@ -57,13 +38,11 @@ public class IndexCreationListener implements ApplicationListener<MappingContext
private static final Logger log = LoggerFactory.getLogger(IndexCreationListener.class);
private Map<String, CompoundIndex> compoundIndexes = new HashMap<String, CompoundIndex>();
private Map<String, Indexed> fieldIndexes = new HashMap<String, Indexed>();
private ApplicationContext applicationContext;
private MongoTemplate mongoTemplate;
@Autowired
private IndexCreationHelper indexCreationHelper;
private ExecutorService worker = Executors.newFixedThreadPool(1);
private LinkedBlockingQueue<MappingContextEvent> mappingEvents = new LinkedBlockingQueue<MappingContextEvent>();
private Set<Class<?>> classesSeen = Collections.newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>());
public IndexCreationListener() {
worker.submit(new IndexCreationWorker());
@@ -73,12 +52,12 @@ public class IndexCreationListener implements ApplicationListener<MappingContext
this.applicationContext = applicationContext;
}
public MongoTemplate getMongoTemplate() {
return mongoTemplate;
public IndexCreationHelper getIndexCreationHelper() {
return indexCreationHelper;
}
public void setMongoTemplate(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
public void setIndexCreationHelper(IndexCreationHelper indexCreationHelper) {
this.indexCreationHelper = indexCreationHelper;
}
public void onApplicationEvent(MappingContextEvent event) {
@@ -109,93 +88,10 @@ public class IndexCreationListener implements ApplicationListener<MappingContext
}
if (event.getPersistentEntity() instanceof MongoPersistentEntity<?>) {
MongoPersistentEntity<?> entity = (MongoPersistentEntity<?>) event.getPersistentEntity();
Class<?> type = event.getTypeInformation().getType();
if (!classesSeen.contains(type)) {
if (log.isDebugEnabled()) {
log.debug("Analyzing class " + type + " for index information.");
}
// Check for special collection setting
if (type.isAnnotationPresent(Document.class)) {
Document doc = type.getAnnotation(Document.class);
String collection = doc.collection();
if ("".equals(collection)) {
collection = type.getSimpleName().toLowerCase();
}
entity.setCollection(collection);
}
// Make sure indexes get created
if (type.isAnnotationPresent(CompoundIndexes.class)) {
CompoundIndexes indexes = type.getAnnotation(CompoundIndexes.class);
for (CompoundIndex index : indexes.value()) {
String indexColl = index.collection();
if ("".equals(indexColl)) {
indexColl = type.getSimpleName().toLowerCase();
}
if (!compoundIndexes.containsKey(indexColl)) {
ensureIndex(indexColl, index.name(), index.def(), index.direction(), index.unique(), index.dropDups(), index.sparse());
if (log.isDebugEnabled()) {
log.debug("Created compound index " + index);
}
compoundIndexes.put(indexColl, index);
}
}
}
entity.doWithProperties(new PropertyHandler() {
public void doWithPersistentProperty(PersistentProperty persistentProperty) {
Field field = persistentProperty.getField();
if (field.isAnnotationPresent(Indexed.class)) {
Indexed index = field.getAnnotation(Indexed.class);
String collection = index.collection();
if ("".equals(collection)) {
collection = field.getName();
}
if (!fieldIndexes.containsKey(collection)) {
ensureIndex(collection, index.name(), null, index.direction(), index.unique(), index.dropDups(), index.sparse());
if (log.isDebugEnabled()) {
log.debug("Created property index " + index);
}
fieldIndexes.put(collection, index);
}
}
}
});
classesSeen.add(type);
}
indexCreationHelper.checkForIndexes(entity);
}
}
}
protected void ensureIndex(String collection,
final String name,
final String def,
final IndexDirection direction,
final boolean unique,
final boolean dropDups,
final boolean sparse) {
mongoTemplate.execute(collection, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
DBObject defObj;
if (null != def) {
defObj = (DBObject) JSON.parse(def);
} else {
defObj = new BasicDBObject();
defObj.put(name, (direction == IndexDirection.ASCENDING ? 1 : -1));
}
DBObject opts = new BasicDBObject();
if (!"".equals(name)) {
opts.put("name", name);
}
opts.put("dropDups", dropDups);
opts.put("sparse", sparse);
opts.put("unique", unique);
collection.ensureIndex(defObj, opts);
return null;
}
});
}
}
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* 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.document.mongodb.mapping.index;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
import com.mongodb.util.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.CollectionCallback;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.index.CompoundIndex;
import org.springframework.data.document.mongodb.index.CompoundIndexes;
import org.springframework.data.document.mongodb.index.IndexDirection;
import org.springframework.data.document.mongodb.index.Indexed;
import org.springframework.data.document.mongodb.mapping.Document;
import org.springframework.data.document.mongodb.mapping.MongoPersistentEntity;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.PersistentProperty;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class IndexCreationHelper implements ApplicationContextAware, InitializingBean {
private static final Logger log = LoggerFactory.getLogger(IndexCreationHelper.class);
private Map<String, CompoundIndex> compoundIndexes = new HashMap<String, CompoundIndex>();
private Map<String, Indexed> fieldIndexes = new HashMap<String, Indexed>();
private Set<Class<?>> classesSeen = Collections.newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>());
private ApplicationContext applicationContext;
private MongoTemplate mongoTemplate;
public IndexCreationHelper() {
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void afterPropertiesSet() throws Exception {
}
public MongoTemplate getMongoTemplate() {
return mongoTemplate;
}
public void setMongoTemplate(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
public void checkForIndexes(MongoPersistentEntity<?> entity) {
Class<?> type = entity.getType();
if (!classesSeen.contains(type)) {
if (log.isDebugEnabled()) {
log.debug("Analyzing class " + type + " for index information.");
}
// Check for special collection setting
if (type.isAnnotationPresent(Document.class)) {
Document doc = type.getAnnotation(Document.class);
String collection = doc.collection();
if ("".equals(collection)) {
collection = type.getSimpleName().toLowerCase();
}
entity.setCollection(collection);
}
// Make sure indexes get created
if (type.isAnnotationPresent(CompoundIndexes.class)) {
CompoundIndexes indexes = type.getAnnotation(CompoundIndexes.class);
for (CompoundIndex index : indexes.value()) {
String indexColl = index.collection();
if ("".equals(indexColl)) {
indexColl = type.getSimpleName().toLowerCase();
}
if (!compoundIndexes.containsKey(indexColl)) {
ensureIndex(indexColl, index.name(), index.def(), index.direction(), index.unique(), index.dropDups(), index.sparse());
if (log.isDebugEnabled()) {
log.debug("Created compound index " + index);
}
compoundIndexes.put(indexColl, index);
}
}
}
entity.doWithProperties(new PropertyHandler() {
public void doWithPersistentProperty(PersistentProperty persistentProperty) {
Field field = persistentProperty.getField();
if (field.isAnnotationPresent(Indexed.class)) {
Indexed index = field.getAnnotation(Indexed.class);
String collection = index.collection();
if ("".equals(collection)) {
collection = field.getName();
}
if (!fieldIndexes.containsKey(collection)) {
ensureIndex(collection, index.name(), null, index.direction(), index.unique(), index.dropDups(), index.sparse());
if (log.isDebugEnabled()) {
log.debug("Created property index " + index);
}
fieldIndexes.put(collection, index);
}
}
}
});
classesSeen.add(type);
}
}
protected void ensureIndex(String collection,
final String name,
final String def,
final IndexDirection direction,
final boolean unique,
final boolean dropDups,
final boolean sparse) {
mongoTemplate.execute(collection, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
DBObject defObj;
if (null != def) {
defObj = (DBObject) JSON.parse(def);
} else {
defObj = new BasicDBObject();
defObj.put(name, (direction == IndexDirection.ASCENDING ? 1 : -1));
}
DBObject opts = new BasicDBObject();
if (!"".equals(name)) {
opts.put("name", name);
}
opts.put("dropDups", dropDups);
opts.put("sparse", sparse);
opts.put("unique", unique);
collection.ensureIndex(defObj, opts);
return null;
}
});
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* 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.document.mongodb.mapping;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MappingConverterTests {
}

View File

@@ -26,8 +26,6 @@ import java.util.Map;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -44,7 +42,6 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:mapping.xml")
@Ignore
public class MappingTests {
@Autowired
@@ -53,8 +50,6 @@ public class MappingTests {
MongoTemplate template;
@Autowired
MappingMongoConverter mappingConverter;
@Autowired
InsertEventListener insertEventListener;
@Test
public void setUp() {
@@ -148,11 +143,6 @@ public class MappingTests {
assertNotNull(p.getId());
}
@Test
public void testEventHandling() {
assertThat(insertEventListener.getCount(), greaterThan(0));
}
@Test
public void testReadEntity() {
List<Person> result = template.find(new Query(Criteria.where("ssn").is(123456789)), Person.class);

View File

@@ -6,7 +6,7 @@ log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.category.org.apache.activemq=ERROR
log4j.category.org.springframework.batch=DEBUG
log4j.category.org.springframework.data.document.mongodb.mapping=DEBUG
log4j.category.org.springframework.data.document.mongodb=DEBUG
log4j.category.org.springframework.transaction=INFO
log4j.category.org.hibernate.SQL=DEBUG

View File

@@ -18,6 +18,4 @@
<bean class="org.springframework.data.document.mongodb.MongoExceptionTranslator"/>
<bean id="insertEventListener" class="org.springframework.data.document.mongodb.mapping.InsertEventListener"/>
</beans>