From 609097c0dcc9fef07b82f8a8b272a76a0edcc21b Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Mon, 16 Jan 2012 18:19:15 +0000 Subject: [PATCH] DATAMONGO-379 - Improved entity instantiation. Huge refactoring of the way MappingMongoConverter instantiates entities. The constructor arguments now have to mirror a property exactly in terms of name. Thus we can pick up mapping information from the property to lookup the correct value from the source document. The @Value annotation can be used to either inject completely arbitrary values into the instance (e.g. by referring to a Spring bean) or simply define an expression against DBObject's fields: class Sample { String foo; String bar; Sample(String foo, @Value("#root._bar") String bar) { this.foo = foo; this.bar = bar; } } trying to create an instance of this class from { "foo" : "FOO" } -> new Sample("FOO", null) { "_bar" : "BAR" } -> new Sample(null, "BAR"). --- .../convert/DBObjectPropertyAccessor.java | 55 ++++++ .../core/convert/MappedConstructor.java | 168 ++++++++++++++++++ .../core/convert/MappingMongoConverter.java | 104 ++++++----- .../data/mongodb/core/MongoTemplateTests.java | 16 +- .../MappingMongoConverterUnitTests.java | 99 +++++++++-- .../mongodb/core/geo/GeoSpatialTests.java | 1 - .../core/mapping/GenericMappingTests.java | 13 +- .../mongodb/core/mapping/MappingTests.java | 16 +- 8 files changed, 388 insertions(+), 84 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DBObjectPropertyAccessor.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappedConstructor.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DBObjectPropertyAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DBObjectPropertyAccessor.java new file mode 100644 index 000000000..85485a141 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DBObjectPropertyAccessor.java @@ -0,0 +1,55 @@ +/* + * 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.convert; + +import java.util.Map; + +import org.springframework.context.expression.MapAccessor; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.PropertyAccessor; +import org.springframework.expression.TypedValue; + +import com.mongodb.DBObject; + +/** + * {@link PropertyAccessor} to allow entity based field access to {@link DBObject}s. + * + * @author Oliver Gierke + */ +class DBObjectPropertyAccessor extends MapAccessor { + + static MapAccessor INSTANCE = new DBObjectPropertyAccessor(); + + @Override + public Class[] getSpecificTargetClasses() { + return new Class[] { DBObject.class }; + } + + @Override + public boolean canRead(EvaluationContext context, Object target, String name) { + return true; + } + + @Override + @SuppressWarnings("unchecked") + public TypedValue read(EvaluationContext context, Object target, String name) { + + Map source = (Map) target; + + Object value = source.get(name); + return value == null ? TypedValue.NULL : new TypedValue(value); + } +} \ No newline at end of file diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappedConstructor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappedConstructor.java new file mode 100644 index 000000000..bc1f01152 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappedConstructor.java @@ -0,0 +1,168 @@ +/* + * 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.convert; + +import java.util.HashSet; +import java.util.Set; + +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PreferredConstructor; +import org.springframework.data.mapping.PreferredConstructor.Parameter; +import org.springframework.data.mapping.PropertyPath; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mapping.context.PersistentPropertyPath; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.data.util.TypeInformation; +import org.springframework.util.Assert; + +/** + * Abstraction for a {@link PreferredConstructor} alongside mapping information. + * + * @author Oliver Gierke + */ +class MappedConstructor { + + private final Set parameters; + + /** + * Creates a new {@link MappedConstructor} from the given {@link MongoPersistentEntity} and {@link MappingContext}. + * + * @param entity must not be {@literal null}. + * @param context must not be {@literal null}. + */ + public MappedConstructor(MongoPersistentEntity entity, + MappingContext, MongoPersistentProperty> context) { + + Assert.notNull(entity); + Assert.notNull(context); + + this.parameters = new HashSet(); + + for (Parameter parameter : entity.getPreferredConstructor().getParameters()) { + parameters.add(new MappedParameter(parameter, entity, context)); + } + } + + /** + * Returns whether the given {@link PersistentProperty} is referenced in a constructor argument of the + * {@link PersistentEntity} backing this {@link MappedConstructor}. + * + * @param property must not be {@literal null}. + * @return + */ + public boolean isConstructorParameter(PersistentProperty property) { + + Assert.notNull(property); + + for (MappedConstructor.MappedParameter parameter : parameters) { + if (parameter.maps(property)) { + return true; + } + } + + return false; + } + + /** + * Returns the {@link MappedParameter} for the given {@link Parameter}. + * + * @param parameter must not be {@literal null}. + * @return + */ + public MappedParameter getFor(Parameter parameter) { + + for (MappedParameter mappedParameter : parameters) { + if (mappedParameter.parameter.equals(parameter)) { + return mappedParameter; + } + } + + throw new IllegalStateException(String.format("Didn't find a MappedParameter for %s!", parameter.toString())); + } + + /** + * Abstraction of a {@link Parameter} alongside mapping information. + * + * @author Oliver Gierke + */ + static class MappedParameter { + + private final MongoPersistentProperty property; + private final Parameter parameter; + + /** + * Creates a new {@link MappedParameter} for the given {@link Parameter}, {@link MongoPersistentProperty} and + * {@link MappingContext}. + * + * @param parameter must not be {@literal null}. + * @param entity must not be {@literal null}. + * @param context must not be {@literal null}. + */ + public MappedParameter(Parameter parameter, MongoPersistentEntity entity, + MappingContext, ? extends MongoPersistentProperty> context) { + + Assert.notNull(parameter); + Assert.notNull(entity); + Assert.notNull(context); + + this.parameter = parameter; + + PropertyPath propertyPath = PropertyPath.from(parameter.getName(), entity.getType()); + PersistentPropertyPath path = context.getPersistentPropertyPath(propertyPath); + this.property = path == null ? null : path.getLeafProperty(); + } + + /** + * Returns whether there is a SpEL expression configured for this parameter. + * + * @return + */ + public boolean hasSpELExpression() { + return parameter.getKey() != null; + } + + /** + * Returns the field name to be used to lookup the value which in turn shall be converted into the constructor + * parameter. + * + * @return + */ + public String getFieldName() { + return property.getFieldName(); + } + + /** + * Returns the type of the property backing the {@link Parameter}. + * + * @return + */ + public TypeInformation getPropertyTypeInformation() { + return property.getTypeInformation(); + } + + /** + * Returns whether the given {@link PersistentProperty} is mapped by the parameter. + * + * @param property + * @return + */ + public boolean maps(PersistentProperty property) { + return this.property.equals(property); + } + } +} \ No newline at end of file diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index 08a8f9684..1df55a411 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -37,7 +37,7 @@ import org.springframework.core.convert.support.ConversionServiceFactory; import org.springframework.data.convert.TypeMapper; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.AssociationHandler; -import org.springframework.data.mapping.PreferredConstructor; +import org.springframework.data.mapping.PreferredConstructor.Parameter; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.BeanWrapper; @@ -189,52 +189,17 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App private S read(final MongoPersistentEntity entity, final DBObject dbo) { - final StandardEvaluationContext spelCtx = new StandardEvaluationContext(); - if (null != applicationContext) { + final StandardEvaluationContext spelCtx = new StandardEvaluationContext(dbo); + spelCtx.addPropertyAccessor(DBObjectPropertyAccessor.INSTANCE); + + if (applicationContext != null) { spelCtx.setBeanResolver(new BeanFactoryResolver(applicationContext)); } - if (!(dbo instanceof BasicDBList)) { - String[] keySet = dbo.keySet().toArray(new String[dbo.keySet().size()]); - for (String key : keySet) { - spelCtx.setVariable(key, dbo.get(key)); - } - } - final List ctorParamNames = new ArrayList(); - final MongoPersistentProperty idProperty = entity.getIdProperty(); + final MappedConstructor constructor = new MappedConstructor(entity, mappingContext); - ParameterValueProvider provider = new SpELAwareParameterValueProvider(spelExpressionParser, spelCtx) { - @Override - @SuppressWarnings("unchecked") - public T getParameterValue(PreferredConstructor.Parameter parameter) { - - if (parameter.getKey() != null) { - return super.getParameterValue(parameter); - } - - String name = parameter.getName(); - TypeInformation type = parameter.getType(); - Class rawType = parameter.getRawType(); - String key = idProperty == null ? name : idProperty.getName().equals(name) ? idProperty.getFieldName() : name; - Object obj = dbo.get(key); - - ctorParamNames.add(name); - if (obj instanceof DBRef) { - return read(type, ((DBRef) obj).fetch()); - } else if (obj instanceof BasicDBList) { - BasicDBList objAsDbList = (BasicDBList) obj; - return conversionService.convert(readCollectionOrArray(type, objAsDbList), rawType); - } else if (obj instanceof DBObject) { - return read(type, ((DBObject) obj)); - } else if (null != obj && obj.getClass().isAssignableFrom(rawType)) { - return (T) obj; - } else if (null != obj) { - return conversionService.convert(obj, rawType); - } - - return null; - } - }; + SpELAwareParameterValueProvider delegate = new SpELAwareParameterValueProvider(spelExpressionParser, spelCtx); + ParameterValueProvider provider = new DelegatingParameterValueProvider(constructor, dbo, delegate); final BeanWrapper, S> wrapper = BeanWrapper.create(entity, provider, conversionService); @@ -242,7 +207,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App entity.doWithProperties(new PropertyHandler() { public void doWithPersistentProperty(MongoPersistentProperty prop) { - boolean isConstructorProperty = ctorParamNames.contains(prop.getName()); + boolean isConstructorProperty = constructor.isConstructorParameter(prop); boolean hasValueForProperty = dbo.containsField(prop.getFieldName()); if (!hasValueForProperty || isConstructorProperty) { @@ -892,4 +857,55 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App return dbObject; } + + private class DelegatingParameterValueProvider implements ParameterValueProvider { + + private final DBObject source; + private final ParameterValueProvider delegate; + private final MappedConstructor constructor; + + /** + * {@link ParameterValueProvider} to delegate source object lookup to a {@link SpELAwareParameterValueProvider} in + * case a MappCon + * + * @param constructor must not be {@literal null}. + * @param source must not be {@literal null}. + * @param delegate must not be {@literal null}. + */ + public DelegatingParameterValueProvider(MappedConstructor constructor, DBObject source, + SpELAwareParameterValueProvider delegate) { + + Assert.notNull(constructor); + Assert.notNull(source); + Assert.notNull(delegate); + + this.constructor = constructor; + this.source = source; + this.delegate = delegate; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter) + */ + @SuppressWarnings("unchecked") + public T getParameterValue(Parameter parameter) { + + MappedConstructor.MappedParameter mappedParameter = constructor.getFor(parameter); + Object value = mappedParameter.hasSpELExpression() ? delegate.getParameterValue(parameter) : source + .get(mappedParameter.getFieldName()); + + TypeInformation type = mappedParameter.getPropertyTypeInformation(); + + if (value instanceof DBRef) { + return (T) read(type, ((DBRef) value).fetch()); + } else if (value instanceof BasicDBList) { + return (T) getPotentiallyConvertedSimpleRead(readCollectionOrArray(type, (BasicDBList) value), type.getType()); + } else if (value instanceof DBObject) { + return (T) read(type, (DBObject) value); + } else { + return (T) getPotentiallyConvertedSimpleRead(value, type.getType()); + } + } + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index 8bc06becd..482d137ee 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -1041,7 +1041,7 @@ public class MongoTemplateTests { List testClassList = mappingTemplate.find(new Query(Criteria.where("myDate").is(dateTime.toDate())), TestClass.class); assertThat(testClassList.size(), is(1)); - assertThat(testClassList.get(0).getMyDate(), is(testClass.getMyDate())); + assertThat(testClassList.get(0).myDate, is(testClass.myDate)); } /** @@ -1080,23 +1080,19 @@ public class MongoTemplateTests { assertThat(template.findOne(query(where("id").is(id)), Sample.class), is(nullValue())); } - public class Sample { + public static class Sample { @Id String id; } - public class TestClass { + static class TestClass { - private DateTime myDate; + DateTime myDate; @PersistenceConstructor - public TestClass(DateTime date) { - this.myDate = date; - } - - public DateTime getMyDate() { - return myDate; + TestClass(DateTime myDate) { + this.myDate = myDate; } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java index dbb23be11..3b7360b15 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java @@ -41,9 +41,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.beans.factory.annotation.Value; import org.springframework.core.convert.converter.Converter; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.mapping.model.MappingInstantiationException; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; @@ -274,6 +276,22 @@ public class MappingMongoConverterUnitTests { assertThat(result.firstname, is("Oliver")); } + @Test + public void resolvesNestedComplexTypeForConstructorCorrectly() { + + DBObject address = new BasicDBObject("street", "110 Southwark Street"); + address.put("city", "London"); + + BasicDBList addresses = new BasicDBList(); + addresses.add(address); + + DBObject person = new BasicDBObject("firstname", "Oliver"); + person.put("addresses", addresses); + + Person result = converter.read(Person.class, person); + assertThat(result.addresses, is(notNullValue())); + } + /** * @see DATAMONGO-145 */ @@ -873,17 +891,55 @@ public class MappingMongoConverterUnitTests { assertThat(((Collection) contacts), hasItem(nullValue())); } - class GenericType { + /** + * @see DATAMONGO-379 + */ + @Test + public void considersDefaultingExpressionsAtConstructorArguments() { + + DBObject dbObject = new BasicDBObject("foo", "bar"); + dbObject.put("foobar", 2.5); + + DefaultedConstructorArgument result = converter.read(DefaultedConstructorArgument.class, dbObject); + assertThat(result.bar, is(-1)); + } + + /** + * @see DATAMONGO-379 + */ + @Test + public void usesDocumentFieldIfReferencedInAtValue() { + + DBObject dbObject = new BasicDBObject("foo", "bar"); + dbObject.put("something", 37); + dbObject.put("foobar", 2.5); + + DefaultedConstructorArgument result = converter.read(DefaultedConstructorArgument.class, dbObject); + assertThat(result.bar, is(37)); + } + + /** + * @see DATAMONGO-379 + */ + @Test(expected = MappingInstantiationException.class) + public void rejectsNotFoundConstructorParameterForPrimitiveType() { + + DBObject dbObject = new BasicDBObject("foo", "bar"); + + converter.read(DefaultedConstructorArgument.class, dbObject); + } + + static class GenericType { T content; } - class ClassWithEnumProperty { + static class ClassWithEnumProperty { SampleEnum sampleEnum; List enums; } - enum SampleEnum { + static enum SampleEnum { FIRST { @Override void method() { @@ -899,7 +955,7 @@ public class MappingMongoConverterUnitTests { abstract void method(); } - class Address { + static class Address { String street; String city; } @@ -908,7 +964,7 @@ public class MappingMongoConverterUnitTests { } - public static class Person implements Contact { + static class Person implements Contact { LocalDate birthDate; @Field("foo") @@ -926,46 +982,46 @@ public class MappingMongoConverterUnitTests { } } - class ClassWithSortedMap { + static class ClassWithSortedMap { SortedMap map; } - class ClassWithMapProperty { + static class ClassWithMapProperty { Map map; Map> mapOfLists; Map mapOfObjects; } - class ClassWithNestedMaps { + static class ClassWithNestedMaps { Map>> nestedMaps; } - class BirthDateContainer { + static class BirthDateContainer { LocalDate birthDate; } - class BigDecimalContainer { + static class BigDecimalContainer { BigDecimal value; Map map; List collection; } - class CollectionWrapper { + static class CollectionWrapper { List contacts; List> strings; List> listOfMaps; } - class LocaleWrapper { + static class LocaleWrapper { Locale locale; } - class ClassWithBigIntegerId { + static class ClassWithBigIntegerId { @Id BigInteger id; } - class A { + static class A { String valueType; T value; @@ -976,12 +1032,25 @@ public class MappingMongoConverterUnitTests { } } - class ClassWithIntId { + static class ClassWithIntId { @Id int id; } + static class DefaultedConstructorArgument { + + String foo; + int bar; + double foobar; + + DefaultedConstructorArgument(String foo, @Value("#root.something ?: -1") int bar, double foobar) { + this.foo = foo; + this.bar = bar; + this.foobar = foobar; + } + } + private class LocalDateToDateConverter implements Converter { public Date convert(LocalDate source) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java index d4f241cc3..1959c5352 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java @@ -54,7 +54,6 @@ import com.mongodb.WriteConcern; * Modified from https://github.com/deftlabs/mongo-java-geospatial-example * * @author Mark Pollack - * */ public class GeoSpatialTests { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GenericMappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GenericMappingTests.java index d7eebf2d1..8ea91b954 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GenericMappingTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GenericMappingTests.java @@ -20,8 +20,7 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import java.util.Collections; -import com.mongodb.BasicDBObject; -import com.mongodb.DBObject; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -30,7 +29,9 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBObject; /** * Unit tests for testing the mapping works with generic types. @@ -85,15 +86,15 @@ public class GenericMappingTests { assertThat(result.container.content, is("Foo!")); } - public class StringWrapper extends Wrapper { + static class StringWrapper extends Wrapper { } - public class Wrapper { + static class Wrapper { Container container; } - public class Container { + static class Container { T content; } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java index e4a389b07..7386bce73 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java @@ -28,11 +28,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import com.mongodb.DB; -import com.mongodb.DBCollection; -import com.mongodb.DBObject; -import com.mongodb.Mongo; -import com.mongodb.MongoException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bson.types.ObjectId; @@ -46,12 +41,17 @@ import org.springframework.data.mongodb.MongoCollectionUtils; import org.springframework.data.mongodb.core.CollectionCallback; import org.springframework.data.mongodb.core.MongoDbUtils; import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Order; import org.springframework.data.mongodb.core.query.Query; import org.springframework.test.util.ReflectionTestUtils; +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; +import com.mongodb.Mongo; +import com.mongodb.MongoException; + /** * @author Jon Brisbin */ @@ -511,7 +511,7 @@ public class MappingTests { assertThat(result.items.get(0).id, is(items.id)); } - class Container { + static class Container { @Id final String id; @@ -526,7 +526,7 @@ public class MappingTests { List items; } - class Item { + static class Item { @Id final String id;