DATAMONGO-2357 - Fix read/write for MongoDB client.model GeoJSON types.
We now consider native GeoJSON types of the MongoDB client during conversion passing on the raw values to the driver when writing and using the configured MongoDB codecs on read. Original pull request: #786.
This commit is contained in:
committed by
Mark Paluch
parent
9a062d53f3
commit
ee6048e289
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<? extends MongoPersistentEntity<?>, 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<? extends Codec<? extends S>> 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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -407,6 +407,7 @@ public class SessionBoundMongoTemplateTests {
|
||||
|
||||
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
|
||||
converter.setCustomConversions(conversions);
|
||||
converter.setCodecRegistryProvider(factory);
|
||||
converter.afterPropertiesSet();
|
||||
|
||||
return converter;
|
||||
|
||||
Reference in New Issue
Block a user