From f1e961a1ee4295d45513385abe86ea4df1a87298 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 28 Mar 2013 16:24:07 +0100 Subject: [PATCH] DATAMONGO-607 - Introduced FieldNamingStrategy SPI interface. MongoMappingContext can now get a FieldNamingStrategy configured to allow the customization of field names used to persist property values to in case *no manual mapping is defined* (e.g. through @Field). The default strategy will simply use the property name as it did before. We now also expose a abbreviate-field-names attribute on the XML namespace element to transparently register a CamelCaseAbbreviatingFieldNamingStrategy which abbreviates the property's name to the first letters of its camel-case structure. A property fooBar would then be persisted to a field named fb. If you're not using the XML namespace simply configure the strategy on your MongoMappingContext instance. To avoid field name mapping ambiguities being introduced through a custom FieldNamingStrategy (imagine the camel-case strategy just mentioned and two properties lastname and level which would both map to l) the PersistentEntity implementation verifies the mapping metadata and throws an exception in case an ambiguity is found. --- .../config/MappingMongoConverterParser.java | 9 +- .../mapping/BasicMongoPersistentEntity.java | 55 +- .../mapping/BasicMongoPersistentProperty.java | 28 +- .../CachingMongoPersistentProperty.java | 7 +- ...elCaseAbbreviatingFieldNamingStrategy.java | 46 ++ .../core/mapping/FieldNamingStrategy.java | 36 ++ .../core/mapping/MongoMappingContext.java | 18 +- .../PropertyNameFieldNamingStrategy.java | 35 ++ .../main/resources/META-INF/spring.schemas | 3 +- .../data/mongodb/config/spring-mongo-1.3.xsd | 493 ++++++++++++++++++ ...gMongoConverterParserIntegrationTests.java | 20 +- .../BasicMongoPersistentEntityUnitTests.java | 4 +- ...BasicMongoPersistentPropertyUnitTests.java | 66 ++- ...reviatingFieldNamingStrategyUnitTests.java | 51 ++ .../mapping/MongoMappingContextUnitTests.java | 52 ++ .../test/resources/namespace/converter.xml | 2 + 16 files changed, 906 insertions(+), 19 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CamelCaseAbbreviatingFieldNamingStrategy.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/FieldNamingStrategy.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/PropertyNameFieldNamingStrategy.java create mode 100644 spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.3.xsd create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/CamelCaseAbbreviatingFieldNamingStrategyUnitTests.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java index 94fcc7df4..3ca46ed81 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2012 the original author or authors. + * Copyright 2011-2013 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. @@ -54,6 +54,7 @@ import org.springframework.data.mapping.context.MappingContextIsNewStrategyFacto import org.springframework.data.mongodb.core.convert.CustomConversions; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator; +import org.springframework.data.mongodb.core.mapping.CamelCaseAbbreviatingFieldNamingStrategy; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener; @@ -200,6 +201,12 @@ public class MappingMongoConverterParser implements BeanDefinitionParser { mappingContextBuilder.addPropertyValue("simpleTypeHolder", simpleTypesDefinition); } + String abbreviateFieldNames = element.getAttribute("abbreviate-field-names"); + if ("true".equals(abbreviateFieldNames)) { + mappingContextBuilder.addPropertyValue("fieldNamingStrategy", new RootBeanDefinition( + CamelCaseAbbreviatingFieldNamingStrategy.class)); + } + ctxRef = converterId + "." + MAPPING_CONTEXT; parserContext.registerBeanComponent(componentDefinitionBuilder.getComponent(mappingContextBuilder, ctxRef)); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java index 1f16d43ab..589e42115 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2012 the original author or authors. + * Copyright 2011-2013 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. @@ -16,13 +16,19 @@ package org.springframework.data.mongodb.core.mapping; import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.expression.BeanFactoryAccessor; import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.data.mapping.Association; +import org.springframework.data.mapping.AssociationHandler; +import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.model.BasicPersistentEntity; +import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mongodb.MongoCollectionUtils; import org.springframework.data.util.TypeInformation; import org.springframework.expression.Expression; @@ -41,6 +47,7 @@ import org.springframework.util.StringUtils; public class BasicMongoPersistentEntity extends BasicPersistentEntity implements MongoPersistentEntity, ApplicationContextAware { + private static final String AMBIGUOUS_FIELD_MAPPING = "Ambiguous field mapping detected! Both %s and %s map to the same field name %s! Disambiguate using @Field annotation!"; private final String collection; private final SpelExpressionParser parser; private final StandardEvaluationContext context; @@ -89,6 +96,19 @@ public class BasicMongoPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity, + AssociationHandler { + + private final Map properties = new HashMap(); + + public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) { + assertUniqueness(persistentProperty); + } + + public void doWithAssociation(Association association) { + assertUniqueness(association.getInverse()); + } + + private void assertUniqueness(MongoPersistentProperty property) { + + String fieldName = property.getFieldName(); + MongoPersistentProperty existingProperty = properties.get(fieldName); + + if (existingProperty != null) { + throw new MappingException(String.format(AMBIGUOUS_FIELD_MAPPING, property.toString(), + existingProperty.toString(), fieldName)); + } + + properties.put(fieldName, property); + } + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java index b147cc390..55180cc20 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2012 the original author or authors. + * Copyright 2011-2013 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. @@ -26,6 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; +import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; @@ -60,6 +61,8 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope CAUSE_FIELD = ReflectionUtils.findField(Throwable.class, "cause"); } + private final FieldNamingStrategy fieldNamingStrategy; + /** * Creates a new {@link BasicMongoPersistentProperty}. * @@ -67,10 +70,14 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope * @param propertyDescriptor * @param owner * @param simpleTypeHolder + * @param fieldNamingStrategy */ public BasicMongoPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, - MongoPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { + MongoPersistentEntity owner, SimpleTypeHolder simpleTypeHolder, FieldNamingStrategy fieldNamingStrategy) { + super(field, propertyDescriptor, owner, simpleTypeHolder); + this.fieldNamingStrategy = fieldNamingStrategy == null ? PropertyNameFieldNamingStrategy.INSTANCE + : fieldNamingStrategy; if (isIdProperty() && getFieldName() != ID_FIELD_NAME) { LOG.warn("Customizing field name for id property not allowed! Custom name will not be considered!"); @@ -113,9 +120,20 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope return ID_FIELD_NAME; } - org.springframework.data.mongodb.core.mapping.Field annotation = getField().getAnnotation( - org.springframework.data.mongodb.core.mapping.Field.class); - return annotation != null && StringUtils.hasText(annotation.value()) ? annotation.value() : field.getName(); + org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation(org.springframework.data.mongodb.core.mapping.Field.class); + + if (annotation != null && StringUtils.hasText(annotation.value())) { + return annotation.value(); + } + + String fieldName = fieldNamingStrategy.getFieldName(this); + + if (!StringUtils.hasText(fieldName)) { + throw new MappingException(String.format("Invalid (null or empty) field name returned for property %s by %s!", + this, fieldNamingStrategy.getClass())); + } + + return fieldName; } /* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java index 466303a56..c91a5de5e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2012 the original author or authors. + * Copyright 2011-2013 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. @@ -38,10 +38,11 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty * @param propertyDescriptor * @param owner * @param simpleTypeHolder + * @param fieldNamingStrategy */ public CachingMongoPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, - MongoPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { - super(field, propertyDescriptor, owner, simpleTypeHolder); + MongoPersistentEntity owner, SimpleTypeHolder simpleTypeHolder, FieldNamingStrategy fieldNamingStrategy) { + super(field, propertyDescriptor, owner, simpleTypeHolder, fieldNamingStrategy); } /* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CamelCaseAbbreviatingFieldNamingStrategy.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CamelCaseAbbreviatingFieldNamingStrategy.java new file mode 100644 index 000000000..08dfa9cfe --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CamelCaseAbbreviatingFieldNamingStrategy.java @@ -0,0 +1,46 @@ +/* + * Copyright 2013 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.mapping; + +import java.util.Locale; + +/** + * {@link FieldNamingStrategy} that abbreviates field names by using the very first letter of the camel case parts of + * the {@link MongoPersistentProperty}'s name. + * + * @since 1.3 + * @author Oliver Gierke + */ +public class CamelCaseAbbreviatingFieldNamingStrategy implements FieldNamingStrategy { + + private static final String CAMEL_CASE_PATTERN = "(?, MongoPersistentProperty> implements ApplicationContextAware { + private static final FieldNamingStrategy DEFAULT_NAMING_STRATEGY = PropertyNameFieldNamingStrategy.INSTANCE; + + private FieldNamingStrategy fieldNamingStrategy = DEFAULT_NAMING_STRATEGY; private ApplicationContext context; /** @@ -46,6 +49,17 @@ public class MongoMappingContext extends AbstractMappingContext owner, SimpleTypeHolder simpleTypeHolder) { - return new CachingMongoPersistentProperty(field, descriptor, owner, simpleTypeHolder); + return new CachingMongoPersistentProperty(field, descriptor, owner, simpleTypeHolder, fieldNamingStrategy); } /* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/PropertyNameFieldNamingStrategy.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/PropertyNameFieldNamingStrategy.java new file mode 100644 index 000000000..cee9c648d --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/PropertyNameFieldNamingStrategy.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013 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.mapping; + +/** + * {@link FieldNamingStrategy} simply using the {@link MongoPersistentProperty}'s name. + * + * @since 1.3 + * @author Oliver Gierke + */ +public enum PropertyNameFieldNamingStrategy implements FieldNamingStrategy { + + INSTANCE; + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.mapping.FieldNamingStrategy#getFieldName(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty) + */ + public String getFieldName(MongoPersistentProperty property) { + return property.getName(); + } +} diff --git a/spring-data-mongodb/src/main/resources/META-INF/spring.schemas b/spring-data-mongodb/src/main/resources/META-INF/spring.schemas index 0e2202232..ebad3c5ac 100644 --- a/spring-data-mongodb/src/main/resources/META-INF/spring.schemas +++ b/spring-data-mongodb/src/main/resources/META-INF/spring.schemas @@ -1,4 +1,5 @@ http\://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd=org/springframework/data/mongodb/config/spring-mongo-1.0.xsd http\://www.springframework.org/schema/data/mongo/spring-mongo-1.1.xsd=org/springframework/data/mongodb/config/spring-mongo-1.1.xsd http\://www.springframework.org/schema/data/mongo/spring-mongo-1.2.xsd=org/springframework/data/mongodb/config/spring-mongo-1.2.xsd -http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-1.2.xsd +http\://www.springframework.org/schema/data/mongo/spring-mongo-1.3.xsd=org/springframework/data/mongodb/config/spring-mongo-1.3.xsd +http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-1.3.xsd diff --git a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.3.xsd b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.3.xsd new file mode 100644 index 000000000..59019663c --- /dev/null +++ b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.3.xsd @@ -0,0 +1,493 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The WriteConcern that will be the default value used when asking the MongoDbFactory for a DB object + + + + + + + + + + + + + + The reference to a MongoTemplate. Will default to 'mongoTemplate'. + + + + + + + Enables creation of indexes for queries that get derived from the method name + and thus reference domain class properties. Defaults to false. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The reference to a DbFactory. + + + + + + + + + + + + The reference to a Mongo. Will default to 'mongo'. + + + + + + + The reference to a MappingContext. Will default to 'mappingContext'. + + + + + + + The reference to a MongoTemplate. Will default to 'mongoTemplate'. + + + + + + + Disables JSR-303 validation on MongoDB documents before they are saved. By default it is set to false. + + + + + + + + + + Enables abbreviating the field names for domain class properties to the + first character of their camel case names, e.g. fooBar -> fb. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The WriteConcern that will be the default value used when asking the MongoDbFactory for a DB object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A reference to a custom converter. + + + + + + + + + \ No newline at end of file diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java index 75242761e..cb9af3806 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2012 the original author or authors. + * Copyright 2011-2013 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. @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.config; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.util.Collections; @@ -23,6 +23,7 @@ import java.util.Set; import org.junit.Before; import org.junit.Test; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.core.convert.TypeDescriptor; @@ -31,6 +32,7 @@ import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.io.ClassPathResource; import org.springframework.data.mongodb.core.convert.CustomConversions; import org.springframework.data.mongodb.core.mapping.Account; +import org.springframework.data.mongodb.core.mapping.CamelCaseAbbreviatingFieldNamingStrategy; import org.springframework.data.mongodb.repository.Person; import org.springframework.stereotype.Component; @@ -67,6 +69,20 @@ public class MappingMongoConverterParserIntegrationTests { assertThat(conversions.hasCustomWriteTarget(Account.class), is(true)); } + /** + * @see DATAMONGO-607 + */ + @Test + public void activatesAbbreviatingPropertiesCorrectly() { + + BeanDefinition definition = factory.getBeanDefinition("abbreviatingConverter.mappingContext"); + Object value = definition.getPropertyValues().getPropertyValue("fieldNamingStrategy").getValue(); + + assertThat(value, is(instanceOf(BeanDefinition.class))); + BeanDefinition strategy = (BeanDefinition) value; + assertThat(strategy.getBeanClassName(), is(CamelCaseAbbreviatingFieldNamingStrategy.class.getName())); + } + @Component public static class SampleConverter implements Converter { public DBObject convert(Person source) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntityUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntityUnitTests.java index ce5cd67ad..abf624c6e 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntityUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntityUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011-2013 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. @@ -15,9 +15,9 @@ */ package org.springframework.data.mongodb.core.mapping; -import static org.mockito.Mockito.*; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java index b1c26388f..c8cc202ed 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011-2013 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. @@ -19,10 +19,14 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import java.lang.reflect.Field; +import java.util.Locale; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.springframework.data.annotation.Id; +import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.ClassTypeInformation; import org.springframework.util.ReflectionUtils; @@ -36,6 +40,9 @@ public class BasicMongoPersistentPropertyUnitTests { MongoPersistentEntity entity; + @Rule + public ExpectedException exception = ExpectedException.none(); + @Before public void setup() { entity = new BasicMongoPersistentEntity(ClassTypeInformation.from(Person.class)); @@ -78,8 +85,45 @@ public class BasicMongoPersistentPropertyUnitTests { assertThat(property.usePropertyAccess(), is(true)); } + /** + * @see DATAMONGO-607 + */ + @Test + public void usesCustomFieldNamingStrategyByDefault() throws Exception { + + Field field = ReflectionUtils.findField(Person.class, "lastname"); + + MongoPersistentProperty property = new BasicMongoPersistentProperty(field, null, entity, new SimpleTypeHolder(), + UppercaseFieldNamingStrategy.INSTANCE); + assertThat(property.getFieldName(), is("LASTNAME")); + + field = ReflectionUtils.findField(Person.class, "firstname"); + + property = new BasicMongoPersistentProperty(field, null, entity, new SimpleTypeHolder(), + UppercaseFieldNamingStrategy.INSTANCE); + assertThat(property.getFieldName(), is("foo")); + } + + /** + * @see DATAMONGO-607 + */ + @Test + public void rejectsInvalidValueReturnedByFieldNamingStrategy() { + + Field field = ReflectionUtils.findField(Person.class, "lastname"); + MongoPersistentProperty property = new BasicMongoPersistentProperty(field, null, entity, new SimpleTypeHolder(), + InvalidFieldNamingStrategy.INSTANCE); + + exception.expect(MappingException.class); + exception.expectMessage(InvalidFieldNamingStrategy.class.getName()); + exception.expectMessage(property.toString()); + + property.getFieldName(); + } + private MongoPersistentProperty getPropertyFor(Field field) { - return new BasicMongoPersistentProperty(field, null, entity, new SimpleTypeHolder()); + return new BasicMongoPersistentProperty(field, null, entity, new SimpleTypeHolder(), + PropertyNameFieldNamingStrategy.INSTANCE); } class Person { @@ -94,4 +138,22 @@ public class BasicMongoPersistentPropertyUnitTests { @org.springframework.data.mongodb.core.mapping.Field(order = -20) String ssn; } + + enum UppercaseFieldNamingStrategy implements FieldNamingStrategy { + + INSTANCE; + + public String getFieldName(MongoPersistentProperty property) { + return property.getName().toUpperCase(Locale.US); + } + } + + enum InvalidFieldNamingStrategy implements FieldNamingStrategy { + + INSTANCE; + + public String getFieldName(MongoPersistentProperty property) { + return null; + } + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/CamelCaseAbbreviatingFieldNamingStrategyUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/CamelCaseAbbreviatingFieldNamingStrategyUnitTests.java new file mode 100644 index 000000000..2340e7205 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/CamelCaseAbbreviatingFieldNamingStrategyUnitTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013 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.mapping; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * Unit tests for {@link CamelCaseAbbreviatingFieldNamingStrategy}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class CamelCaseAbbreviatingFieldNamingStrategyUnitTests { + + FieldNamingStrategy strategy = new CamelCaseAbbreviatingFieldNamingStrategy(); + + @Mock + MongoPersistentProperty property; + + @Test + public void foo() { + assertFieldNameForPropertyName("fooBar", "fb"); + assertFieldNameForPropertyName("fooBARFooBar", "fbfb"); + } + + private void assertFieldNameForPropertyName(String propertyName, String fieldName) { + + when(property.getName()).thenReturn(propertyName); + assertThat(strategy.getFieldName(property), is(fieldName)); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoMappingContextUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoMappingContextUnitTests.java index 641e4f241..2a3a42c95 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoMappingContextUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoMappingContextUnitTests.java @@ -21,9 +21,12 @@ import static org.junit.Assert.*; import java.lang.reflect.Field; import java.util.AbstractMap; import java.util.Collections; +import java.util.Locale; import java.util.Map; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @@ -46,6 +49,9 @@ public class MongoMappingContextUnitTests { @Mock ApplicationContext applicationContext; + @Rule + public ExpectedException exception = ExpectedException.none(); + @Test public void addsSelfReferencingPersistentEntityCorrectly() throws Exception { @@ -90,6 +96,41 @@ public class MongoMappingContextUnitTests { assertThat(context.getPersistentEntity(AbstractMap.class), is(nullValue())); } + /** + * @see DATAMONGO-607 + */ + @Test + public void populatesPersistentPropertyWithCustomFieldNamingStrategy() { + + MongoMappingContext context = new MongoMappingContext(); + context.setApplicationContext(applicationContext); + context.setFieldNamingStrategy(new FieldNamingStrategy() { + + public String getFieldName(MongoPersistentProperty property) { + return property.getName().toUpperCase(Locale.US); + } + }); + + MongoPersistentEntity entity = context.getPersistentEntity(Person.class); + assertThat(entity.getPersistentProperty("firstname").getFieldName(), is("FIRSTNAME")); + } + + /** + * @see DATAMONGO-607 + */ + @Test + public void rejectsClassWithAmbiguousFieldMappings() { + + exception.expect(MappingException.class); + exception.expectMessage("firstname"); + exception.expectMessage("lastname"); + exception.expectMessage("foo"); + + MongoMappingContext context = new MongoMappingContext(); + context.setApplicationContext(applicationContext); + context.getPersistentEntity(InvalidPerson.class); + } + class ClassWithMultipleIdProperties { @Id @@ -102,4 +143,15 @@ public class MongoMappingContextUnitTests { Map children; } + + class Person { + + String firstname, lastname; + } + + class InvalidPerson { + + @org.springframework.data.mongodb.core.mapping.Field("foo") + String firstname, lastname; + } } diff --git a/spring-data-mongodb/src/test/resources/namespace/converter.xml b/spring-data-mongodb/src/test/resources/namespace/converter.xml index 45189bcbc..7aef47ceb 100644 --- a/spring-data-mongodb/src/test/resources/namespace/converter.xml +++ b/spring-data-mongodb/src/test/resources/namespace/converter.xml @@ -10,5 +10,7 @@ + +