From 0a0ee417ac9e2fbd57e68eeec063f1be6e3b7324 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 30 Jul 2019 10:55:42 +0200 Subject: [PATCH] DATAMONGO-2328 - Add missing target type conversions for field level type hints. We now support Date to Long, Date to ObjectId, Script to String and other conversions for both reading and writing scenarios. Original pull request: #773. --- .../core/convert/AbstractMongoConverter.java | 21 ++- .../MappingMongoConverterUnitTests.java | 120 +++++++++++++++++- 2 files changed, 135 insertions(+), 6 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java index 834a94fb3..8b3733c1a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java @@ -15,8 +15,12 @@ */ package org.springframework.data.mongodb.core.convert; -import java.math.BigInteger; +import static org.springframework.data.convert.ConverterBuilder.*; +import java.math.BigInteger; +import java.util.Date; + +import org.bson.types.Code; import org.bson.types.ObjectId; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.convert.ConversionService; @@ -93,6 +97,21 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali conversionService.addConverter(BigIntegerToObjectIdConverter.INSTANCE); } + if (!conversionService.canConvert(Date.class, Long.class)) { + conversionService.addConverter(writing(Date.class, Long.class, Date::getTime).getWritingConverter()); + } + + if (!conversionService.canConvert(Long.class, Date.class)) { + conversionService.addConverter(reading(Long.class, Date.class, Date::new).getReadingConverter()); + } + + if (!conversionService.canConvert(ObjectId.class, Date.class)) { + + conversionService.addConverter( + reading(ObjectId.class, Date.class, objectId -> new Date(objectId.getTimestamp())).getReadingConverter()); + } + + conversionService.addConverter(reading(Code.class, String.class, Code::getCode).getReadingConverter()); conversions.registerConvertersIn(conversionService); } 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 82779ceb6..b0567b77a 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 @@ -40,7 +40,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; - import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.ConversionNotSupportedException; import org.springframework.beans.factory.annotation.Value; @@ -1943,6 +1942,16 @@ public class MappingMongoConverterUnitTests { assertThat(target.get("script")).isEqualTo(new Code(source.script)); } + @Test // DATAMONGO-2328 + public void readsScriptAsStringWhenAnnotatedWithFieldTargetType() { + + String reference = "if (a > b) a else b"; + WithExplicitTargetTypes target = converter.read(WithExplicitTargetTypes.class, + new org.bson.Document("script", new Code(reference))); + + assertThat(target.script).isEqualTo(reference); + } + @Test // DATAMONGO-1849 public void mapsCollectionValueToExplicitTargetType() { @@ -1968,6 +1977,96 @@ public class MappingMongoConverterUnitTests { assertThat(target.get("bigDecimal")).isEqualTo(new Decimal128(source.bigDecimal)); } + @Test // DATAMONGO-2328 + public void mapsDateToLongWhenAnnotatedWithFieldTargetType() { + + WithExplicitTargetTypes source = new WithExplicitTargetTypes(); + source.dateAsLong = new Date(); + + org.bson.Document target = new org.bson.Document(); + converter.write(source, target); + + assertThat(target.get("dateAsLong")).isEqualTo(source.dateAsLong.getTime()); + } + + @Test // DATAMONGO-2328 + public void readsLongAsDateWhenAnnotatedWithFieldTargetType() { + + Date reference = new Date(); + WithExplicitTargetTypes target = converter.read(WithExplicitTargetTypes.class, + new org.bson.Document("dateAsLong", reference.getTime())); + + assertThat(target.dateAsLong).isEqualTo(reference); + } + + @Test // DATAMONGO-2328 + public void mapsLongToDateWhenAnnotatedWithFieldTargetType() { + + Date date = new Date(); + WithExplicitTargetTypes source = new WithExplicitTargetTypes(); + source.longAsDate = date.getTime(); + + org.bson.Document target = new org.bson.Document(); + converter.write(source, target); + + assertThat(target.get("longAsDate")).isEqualTo(date); + } + + @Test // DATAMONGO-2328 + public void readsDateAsLongWhenAnnotatedWithFieldTargetType() { + + Date reference = new Date(); + WithExplicitTargetTypes target = converter.read(WithExplicitTargetTypes.class, + new org.bson.Document("longAsDate", reference)); + + assertThat(target.longAsDate).isEqualTo(reference.getTime()); + } + + @Test // DATAMONGO-2328 + public void mapsStringAsBooleanWhenAnnotatedWithFieldTargetType() { + + WithExplicitTargetTypes source = new WithExplicitTargetTypes(); + source.stringAsBoolean = "true"; + + org.bson.Document target = new org.bson.Document(); + converter.write(source, target); + + assertThat(target.get("stringAsBoolean")).isEqualTo(true); + } + + @Test // DATAMONGO-2328 + public void readsBooleanAsStringWhenAnnotatedWithFieldTargetType() { + + WithExplicitTargetTypes target = converter.read(WithExplicitTargetTypes.class, + new org.bson.Document("stringAsBoolean", true)); + + assertThat(target.stringAsBoolean).isEqualTo("true"); + } + + @Test // DATAMONGO-2328 + public void mapsDateAsObjectIdWhenAnnotatedWithFieldTargetType() { + + WithExplicitTargetTypes source = new WithExplicitTargetTypes(); + source.dateAsObjectId = new Date(); + + org.bson.Document target = new org.bson.Document(); + converter.write(source, target); + + // need to compare the the timestamp as ObjectId has an internal counter + assertThat(target.get("dateAsObjectId", ObjectId.class).getTimestamp()) + .isEqualTo(new ObjectId(source.dateAsObjectId).getTimestamp()); + } + + @Test // DATAMONGO-2328 + public void readsObjectIdAsDateWhenAnnotatedWithFieldTargetType() { + + ObjectId reference = new ObjectId(); + WithExplicitTargetTypes target = converter.read(WithExplicitTargetTypes.class, + new org.bson.Document("dateAsObjectId", reference)); + + assertThat(target.dateAsObjectId).isEqualTo(new Date(reference.getTimestamp())); + } + static class GenericType { T content; } @@ -1987,9 +2086,7 @@ public class MappingMongoConverterUnitTests { }, SECOND { @Override - void method() { - - } + void method() {} }; abstract void method(); @@ -2416,7 +2513,20 @@ public class MappingMongoConverterUnitTests { @Field(targetType = FieldType.SCRIPT) // List scripts; - @Field(targetType = FieldType.DECIMAL128) BigDecimal bigDecimal; + @Field(targetType = FieldType.DECIMAL128) // + BigDecimal bigDecimal; + + @Field(targetType = FieldType.INT64) // + Date dateAsLong; + + @Field(targetType = FieldType.DATE_TIME) // + Long longAsDate; + + @Field(targetType = FieldType.BOOLEAN) // + String stringAsBoolean; + + @Field(targetType = FieldType.OBJECT_ID) // + Date dateAsObjectId; } }