diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoClientConfiguration.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoClientConfiguration.java index 48da93298..c5f10730f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoClientConfiguration.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoClientConfiguration.java @@ -105,6 +105,7 @@ public abstract class AbstractMongoClientConfiguration extends MongoConfiguratio DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory()); MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext()); converter.setCustomConversions(customConversions()); + converter.setCodecRegistryProvider(mongoDbFactory()); return converter; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoConfiguration.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoConfiguration.java index 13241b34d..99e5c786d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoConfiguration.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoConfiguration.java @@ -111,6 +111,7 @@ public abstract class AbstractMongoConfiguration extends MongoConfigurationSuppo DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory()); MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext()); converter.setCustomConversions(customConversions()); + converter.setCodecRegistryProvider(mongoDbFactory()); return converter; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfiguration.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfiguration.java index c263df2f7..1b9225490 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfiguration.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfiguration.java @@ -83,6 +83,7 @@ public abstract class AbstractReactiveMongoConfiguration extends MongoConfigurat MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mongoMappingContext()); converter.setCustomConversions(customConversions()); + converter.setCodecRegistryProvider(reactiveMongoDbFactory()); return converter; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 8aa9c46fd..55a12a434 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -2846,6 +2846,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext); converter.setCustomConversions(conversions); + converter.setCodecRegistryProvider(factory); converter.afterPropertiesSet(); return converter; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java index 1e4760951..f445c1eae 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java @@ -2774,7 +2774,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return type == null ? null : mappingContext.getPersistentEntity(type); } - private static MappingMongoConverter getDefaultMongoConverter() { + private MappingMongoConverter getDefaultMongoConverter() { MongoCustomConversions conversions = new MongoCustomConversions(Collections.emptyList()); @@ -2784,6 +2784,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati MappingMongoConverter converter = new MappingMongoConverter(NO_OP_REF_RESOLVER, context); converter.setCustomConversions(conversions); + converter.setCodecRegistryProvider(this.mongoDatabaseFactory); converter.afterPropertiesSet(); return converter; 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 0d0e7a685..1721bd0d8 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 @@ -30,11 +30,13 @@ import java.util.Optional; import java.util.Set; import org.bson.Document; +import org.bson.codecs.Codec; +import org.bson.codecs.DecoderContext; import org.bson.conversions.Bson; +import org.bson.json.JsonReader; import org.bson.types.ObjectId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -57,6 +59,7 @@ import org.springframework.data.mapping.model.PropertyValueProvider; import org.springframework.data.mapping.model.SpELContext; import org.springframework.data.mapping.model.SpELExpressionEvaluator; import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider; +import org.springframework.data.mongodb.CodecRegistryProvider; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; @@ -103,6 +106,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App protected @Nullable ApplicationContext applicationContext; protected MongoTypeMapper typeMapper; protected @Nullable String mapKeyDotReplacement = null; + protected @Nullable CodecRegistryProvider codecRegistryProvider; private SpELContext spELContext; @@ -141,6 +145,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App public MappingMongoConverter(MongoDbFactory mongoDbFactory, MappingContext, MongoPersistentProperty> mappingContext) { this(new DefaultDbRefResolver(mongoDbFactory), mappingContext); + setCodecRegistryProvider(mongoDbFactory); } /** @@ -178,6 +183,17 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App this.mapKeyDotReplacement = mapKeyDotReplacement; } + /** + * Configure a {@link CodecRegistryProvider} that provides native MongoDB {@link org.bson.codecs.Codec codecs} for + * reading values. + * + * @param codecRegistryProvider can be {@literal null}. + * @since 2.2 + */ + public void setCodecRegistryProvider(@Nullable CodecRegistryProvider codecRegistryProvider) { + this.codecRegistryProvider = codecRegistryProvider; + } + /* * (non-Javadoc) * @see org.springframework.data.convert.EntityConverter#getMappingContext() @@ -253,6 +269,16 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App MongoPersistentEntity entity = mappingContext.getPersistentEntity(typeToUse); if (entity == null) { + + if (codecRegistryProvider != null) { + + Optional> codec = codecRegistryProvider.getCodecFor(rawType); + if(codec.isPresent()) { + return codec.get().decode(new JsonReader(target.toJson()), + DecoderContext.builder().build()); + } + } + throw new MappingException(String.format(INVALID_TYPE_TO_READ, target, typeToUse.getType())); } @@ -1650,6 +1676,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App target.spELContext = spELContext; target.setInstantiators(instantiators); target.typeMapper = typeMapper; + target.setCodecRegistryProvider(dbFactory); target.afterPropertiesSet(); return target; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java index f0e24af5a..39163775d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java @@ -33,6 +33,14 @@ import org.bson.types.Symbol; import org.springframework.data.mapping.model.SimpleTypeHolder; import com.mongodb.DBRef; +import com.mongodb.client.model.geojson.Geometry; +import com.mongodb.client.model.geojson.GeometryCollection; +import com.mongodb.client.model.geojson.LineString; +import com.mongodb.client.model.geojson.MultiLineString; +import com.mongodb.client.model.geojson.MultiPoint; +import com.mongodb.client.model.geojson.MultiPolygon; +import com.mongodb.client.model.geojson.Point; +import com.mongodb.client.model.geojson.Polygon; /** * Simple constant holder for a {@link SimpleTypeHolder} enriched with Mongo specific simple types. @@ -80,6 +88,15 @@ public abstract class MongoSimpleTypes { simpleTypes.add(BsonString.class); simpleTypes.add(BsonTimestamp.class); + simpleTypes.add(Geometry.class); + simpleTypes.add(GeometryCollection.class); + simpleTypes.add(LineString.class); + simpleTypes.add(MultiLineString.class); + simpleTypes.add(MultiPoint.class); + simpleTypes.add(MultiPolygon.class); + simpleTypes.add(Point.class); + simpleTypes.add(Polygon.class); + MONGO_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateMappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateMappingTests.java index e9941674f..3477a598a 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateMappingTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateMappingTests.java @@ -16,20 +16,30 @@ package org.springframework.data.mongodb.core; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.mongodb.core.query.Criteria.*; +import static org.springframework.data.mongodb.core.query.Query.*; + +import lombok.Data; + +import java.util.Arrays; import org.bson.Document; import org.junit.Before; 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.dao.DataAccessException; +import org.springframework.data.annotation.Id; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.mongodb.MongoException; import com.mongodb.client.MongoCollection; +import com.mongodb.client.model.geojson.Geometry; +import com.mongodb.client.model.geojson.MultiPolygon; +import com.mongodb.client.model.geojson.PolygonCoordinates; +import com.mongodb.client.model.geojson.Position; /** * Integration test for {@link MongoTemplate}. @@ -65,6 +75,46 @@ public class MongoTemplateMappingTests { checkPersonPersisted(template2); } + @Test // DATAMONGO-2357 + public void writesAndReadsEntityWithNativeMongoGeoJsonTypesCorrectly() { + + WithMongoGeoJson source = new WithMongoGeoJson(); + source.id = "id-2"; + source.multiPolygon = new MultiPolygon(Arrays.asList(new PolygonCoordinates(Arrays.asList(new Position(0, 0), + new Position(0, 1), new Position(1, 1), new Position(1, 0), new Position(0, 0))))); + + template1.save(source); + + assertThat(template1.findOne(query(where("id").is(source.id)), WithMongoGeoJson.class)).isEqualTo(source); + } + + @Test // DATAMONGO-2357 + public void writesAndReadsEntityWithOpenNativeMongoGeoJsonTypesCorrectly() { + + WithOpenMongoGeoJson source = new WithOpenMongoGeoJson(); + source.id = "id-2"; + source.geometry = new MultiPolygon(Arrays.asList(new PolygonCoordinates(Arrays.asList(new Position(0, 0), + new Position(0, 1), new Position(1, 1), new Position(1, 0), new Position(0, 0))))); + + template1.save(source); + + assertThat(template1.findOne(query(where("id").is(source.id)), WithOpenMongoGeoJson.class)).isEqualTo(source); + } + + @Data + static class WithMongoGeoJson { + + @Id String id; + MultiPolygon multiPolygon; + } + + @Data + static class WithOpenMongoGeoJson { + + @Id String id; + Geometry geometry; + } + private void addAndRetrievePerson(MongoTemplate template) { Person person = new Person("Oliver"); person.setAge(25); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateTests.java index 76c4b8a49..92978ce46 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateTests.java @@ -407,6 +407,7 @@ public class SessionBoundMongoTemplateTests { MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext); converter.setCustomConversions(conversions); + converter.setCodecRegistryProvider(factory); converter.afterPropertiesSet(); return converter;