diff --git a/pom.xml b/pom.xml index 95fc8379d9..1e2a897451 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 5.0.0-SNAPSHOT + 5.0.x-GH-4290-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index fc88571622..21588fad2b 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -15,7 +15,7 @@ org.springframework.data spring-data-mongodb-parent - 5.0.0-SNAPSHOT + 5.0.x-GH-4290-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 6f34da5660..6f62b2868d 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 5.0.0-SNAPSHOT + 5.0.x-GH-4290-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java index 8dccced380..7e0365d455 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java @@ -159,7 +159,7 @@ public static class MongoConverterConfigurationAdapter { private static final Set> JAVA_DRIVER_TIME_SIMPLE_TYPES = Set.of(LocalDate.class, LocalTime.class, LocalDateTime.class); private boolean useNativeDriverJavaTimeCodecs = false; - private BigDecimalRepresentation bigDecimals = BigDecimalRepresentation.STRING; + private BigDecimalRepresentation bigDecimals = BigDecimalRepresentation.DECIMAL128; private final List customConverters = new ArrayList<>(); private final PropertyValueConversions internalValueConversion = PropertyValueConversions.simple(it -> {}); @@ -314,7 +314,7 @@ public MongoConverterConfigurationAdapter useSpringDataJavaTimeCodecs() { /** * Configures the representation to for {@link java.math.BigDecimal} and {@link java.math.BigInteger} values in - * MongoDB. Defaults to {@link BigDecimalRepresentation#STRING}. + * MongoDB. Defaults to {@link BigDecimalRepresentation#DECIMAL128}. * * @param representation the representation to use. * @return this. @@ -457,9 +457,10 @@ private boolean hasDefaultPropertyValueConversions() { public enum BigDecimalRepresentation { /** - * Store values as {@link Number#toString() String}. Using strings retains precision but does not support range - * queries. + * @deprecated since 5.0.0 — storing values as {@link Number#toString() String} retains precision, + * but prevents efficient range queries. Prefer {@link #DECIMAL128} for better query support. */ + @Deprecated(since = "5.0.0") STRING, /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/BigDecimalToStringConvertingTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/BigDecimalToStringConvertingTemplateTests.java new file mode 100644 index 0000000000..ec17189bca --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/BigDecimalToStringConvertingTemplateTests.java @@ -0,0 +1,170 @@ +/* + * Copyright 2025 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 + * + * https://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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.mongodb.core.query.Criteria.where; +import static org.springframework.data.mongodb.core.query.Query.query; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.data.auditing.IsNewAwareAuditingHandler; +import org.springframework.data.mapping.context.PersistentEntities; +import org.springframework.data.mongodb.core.MongoTemplateTests.TypeWithNumbers; +import org.springframework.data.mongodb.core.convert.MongoCustomConversions; +import org.springframework.data.mongodb.core.convert.MongoCustomConversions.BigDecimalRepresentation; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; +import org.springframework.data.mongodb.test.util.Client; +import org.springframework.data.mongodb.test.util.MongoClientExtension; +import org.springframework.data.mongodb.test.util.MongoTestTemplate; + +import com.mongodb.client.MongoClient; + +/** + * Tests for {@link MongoTemplate} using string representation of {@link BigInteger} values. + * + * @author Christoph Strobl + */ +@ExtendWith(MongoClientExtension.class) +public class BigDecimalToStringConvertingTemplateTests { + + public static final String DB_NAME = "mongo-template-tests"; + + static @Client MongoClient client; + + @SuppressWarnings("deprecation") MongoTestTemplate template = new MongoTestTemplate(cfg -> { + + cfg.configureDatabaseFactory(it -> { + + it.client(client); + it.defaultDb(DB_NAME); + }); + + cfg.configureConversion(it -> { + it.customConversions( + MongoCustomConversions.create(adapter -> adapter.bigDecimal(BigDecimalRepresentation.STRING))); + }); + + cfg.configureMappingContext(it -> { + it.autocreateIndex(false); + }); + + cfg.configureAuditing(it -> { + it.auditingHandler(ctx -> { + return new IsNewAwareAuditingHandler(PersistentEntities.of(ctx)); + }); + }); + }); + + @AfterEach + public void cleanUp() { + template.flush(); + } + + @Test // DATAMONGO-602 + void testUsingAnInQueryWithBigIntegerId() { + + template.remove(new Query(), PersonWithIdPropertyOfTypeBigInteger.class); + + PersonWithIdPropertyOfTypeBigInteger p1 = new PersonWithIdPropertyOfTypeBigInteger(); + p1.setFirstName("Sven"); + p1.setAge(11); + p1.setId(new BigInteger("2666666666666666665069473312490162649510603601")); + template.insert(p1); + PersonWithIdPropertyOfTypeBigInteger p2 = new PersonWithIdPropertyOfTypeBigInteger(); + p2.setFirstName("Mary"); + p2.setAge(21); + p2.setId(new BigInteger("2666666666666666665069473312490162649510603602")); + template.insert(p2); + PersonWithIdPropertyOfTypeBigInteger p3 = new PersonWithIdPropertyOfTypeBigInteger(); + p3.setFirstName("Ann"); + p3.setAge(31); + p3.setId(new BigInteger("2666666666666666665069473312490162649510603603")); + template.insert(p3); + PersonWithIdPropertyOfTypeBigInteger p4 = new PersonWithIdPropertyOfTypeBigInteger(); + p4.setFirstName("John"); + p4.setAge(41); + p4.setId(new BigInteger("2666666666666666665069473312490162649510603604")); + template.insert(p4); + + Query q1 = new Query(Criteria.where("age").in(11, 21, 41)); + List results1 = template.find(q1, PersonWithIdPropertyOfTypeBigInteger.class); + Query q2 = new Query(Criteria.where("firstName").in("Ann", "Mary")); + List results2 = template.find(q2, PersonWithIdPropertyOfTypeBigInteger.class); + Query q3 = new Query(Criteria.where("id").in(new BigInteger("2666666666666666665069473312490162649510603601"), + new BigInteger("2666666666666666665069473312490162649510603604"))); + List results3 = template.find(q3, PersonWithIdPropertyOfTypeBigInteger.class); + assertThat(results1.size()).isEqualTo(3); + assertThat(results2.size()).isEqualTo(2); + assertThat(results3.size()).isEqualTo(2); + } + + @Test // DATAMONGO-1404 + void updatesBigNumberValueUsingStringComparisonWhenUsingMaxOperator() { + + TypeWithNumbers twn = new TypeWithNumbers(); + + // Note that $max operator uses String comparison for BigDecimal/BigInteger comparison according to BSON sort rules. + // Therefore "80" is considered greater than "700" + twn.bigIntegerVal = new BigInteger("600"); + twn.bigDeciamVal = new BigDecimal("700.0"); + + template.save(twn); + + Update update = new Update()// + .max("bigIntegerVal", new BigInteger("70")) // + .max("bigDeciamVal", new BigDecimal("80")) // + ; + + template.updateFirst(query(where("id").is(twn.id)), update, TypeWithNumbers.class); + + TypeWithNumbers loaded = template.find(query(where("id").is(twn.id)), TypeWithNumbers.class).get(0); + assertThat(loaded.bigIntegerVal).isEqualTo(new BigInteger("70")); + assertThat(loaded.bigDeciamVal).isEqualTo(new BigDecimal("80")); + } + + @Test // DATAMONGO-1404 + void updatesBigNumberValueUsingStringComparisonWhenUsingMinOperator() { + + TypeWithNumbers twn = new TypeWithNumbers(); + + // Note that $max operator uses String comparison for BigDecimal/BigInteger comparison according to BSON sort rules. + // Therefore "80" is considered greater than "700" + twn.bigIntegerVal = new BigInteger("80"); + twn.bigDeciamVal = new BigDecimal("90.0"); + + template.save(twn); + + Update update = new Update()// + .min("bigIntegerVal", new BigInteger("700")) // + .min("bigDeciamVal", new BigDecimal("800")) // + ; + + template.updateFirst(query(where("id").is(twn.id)), update, TypeWithNumbers.class); + + TypeWithNumbers loaded = template.find(query(where("id").is(twn.id)), TypeWithNumbers.class).get(0); + assertThat(loaded.bigIntegerVal).isEqualTo(new BigInteger("700")); + assertThat(loaded.bigDeciamVal).isEqualTo(new BigDecimal("800")); + } + +} 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 5a006bebfe..729551f598 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 @@ -42,6 +42,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.converter.Converter; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; @@ -877,7 +878,7 @@ public void testUsingAnInQueryWithLongId() throws Exception { assertThat(results3.size()).isEqualTo(2); } - @Test // DATAMONGO-602 + @Test // DATAMONGO-602, GH-4920 public void testUsingAnInQueryWithBigIntegerId() throws Exception { template.remove(new Query(), PersonWithIdPropertyOfTypeBigInteger.class); @@ -886,33 +887,7 @@ public void testUsingAnInQueryWithBigIntegerId() throws Exception { p1.setFirstName("Sven"); p1.setAge(11); p1.setId(new BigInteger("2666666666666666665069473312490162649510603601")); - template.insert(p1); - PersonWithIdPropertyOfTypeBigInteger p2 = new PersonWithIdPropertyOfTypeBigInteger(); - p2.setFirstName("Mary"); - p2.setAge(21); - p2.setId(new BigInteger("2666666666666666665069473312490162649510603602")); - template.insert(p2); - PersonWithIdPropertyOfTypeBigInteger p3 = new PersonWithIdPropertyOfTypeBigInteger(); - p3.setFirstName("Ann"); - p3.setAge(31); - p3.setId(new BigInteger("2666666666666666665069473312490162649510603603")); - template.insert(p3); - PersonWithIdPropertyOfTypeBigInteger p4 = new PersonWithIdPropertyOfTypeBigInteger(); - p4.setFirstName("John"); - p4.setAge(41); - p4.setId(new BigInteger("2666666666666666665069473312490162649510603604")); - template.insert(p4); - - Query q1 = new Query(Criteria.where("age").in(11, 21, 41)); - List results1 = template.find(q1, PersonWithIdPropertyOfTypeBigInteger.class); - Query q2 = new Query(Criteria.where("firstName").in("Ann", "Mary")); - List results2 = template.find(q2, PersonWithIdPropertyOfTypeBigInteger.class); - Query q3 = new Query(Criteria.where("id").in(new BigInteger("2666666666666666665069473312490162649510603601"), - new BigInteger("2666666666666666665069473312490162649510603604"))); - List results3 = template.find(q3, PersonWithIdPropertyOfTypeBigInteger.class); - assertThat(results1.size()).isEqualTo(3); - assertThat(results2.size()).isEqualTo(2); - assertThat(results3.size()).isEqualTo(2); + assertThatExceptionOfType(ConversionFailedException.class).isThrownBy(() -> template.insert(p1)); } @Test @@ -3277,7 +3252,6 @@ public void updatesNumericValueCorrectlyWhenUsingMinOperator() { twn.intVal = 400; twn.longVal = 500L; - // Note that $min operator uses String comparison for BigDecimal/BigInteger comparison according to BSON sort rules. twn.bigIntegerVal = new BigInteger("600"); twn.bigDeciamVal = new BigDecimal("700.0"); @@ -3333,7 +3307,6 @@ public void updatesNumericValueCorrectlyWhenUsingMaxOperator() { twn.intVal = 400; twn.longVal = 500L; - // Note that $max operator uses String comparison for BigDecimal/BigInteger comparison according to BSON sort rules. twn.bigIntegerVal = new BigInteger("600"); twn.bigDeciamVal = new BigDecimal("700.0"); @@ -3362,13 +3335,11 @@ public void updatesNumericValueCorrectlyWhenUsingMaxOperator() { assertThat(loaded.bigDeciamVal).isEqualTo(new BigDecimal("790")); } - @Test // DATAMONGO-1404 - public void updatesBigNumberValueUsingStringComparisonWhenUsingMaxOperator() { + @Test // DATAMONGO-1404, GH-4920 + public void updatesBigNumberValueUsingUsingMaxOperator() { TypeWithNumbers twn = new TypeWithNumbers(); - // Note that $max operator uses String comparison for BigDecimal/BigInteger comparison according to BSON sort rules. - // Therefore "80" is considered greater than "700" twn.bigIntegerVal = new BigInteger("600"); twn.bigDeciamVal = new BigDecimal("700.0"); @@ -3382,17 +3353,15 @@ public void updatesBigNumberValueUsingStringComparisonWhenUsingMaxOperator() { template.updateFirst(query(where("id").is(twn.id)), update, TypeWithNumbers.class); TypeWithNumbers loaded = template.find(query(where("id").is(twn.id)), TypeWithNumbers.class).get(0); - assertThat(loaded.bigIntegerVal).isEqualTo(new BigInteger("70")); - assertThat(loaded.bigDeciamVal).isEqualTo(new BigDecimal("80")); + assertThat(loaded.bigIntegerVal).isEqualTo(new BigInteger("600")); + assertThat(loaded.bigDeciamVal).isEqualTo(new BigDecimal("700.0")); } - @Test // DATAMONGO-1404 - public void updatesBigNumberValueUsingStringComparisonWhenUsingMinOperator() { + @Test // DATAMONGO-1404, GH-4920 + public void updatesBigNumberValueWhenUsingMinOperator() { TypeWithNumbers twn = new TypeWithNumbers(); - // Note that $max operator uses String comparison for BigDecimal/BigInteger comparison according to BSON sort rules. - // Therefore "80" is considered greater than "700" twn.bigIntegerVal = new BigInteger("80"); twn.bigDeciamVal = new BigDecimal("90.0"); @@ -3406,8 +3375,8 @@ public void updatesBigNumberValueUsingStringComparisonWhenUsingMinOperator() { template.updateFirst(query(where("id").is(twn.id)), update, TypeWithNumbers.class); TypeWithNumbers loaded = template.find(query(where("id").is(twn.id)), TypeWithNumbers.class).get(0); - assertThat(loaded.bigIntegerVal).isEqualTo(new BigInteger("700")); - assertThat(loaded.bigDeciamVal).isEqualTo(new BigDecimal("800")); + assertThat(loaded.bigIntegerVal).isEqualTo(new BigInteger("80")); + assertThat(loaded.bigDeciamVal).isEqualTo(new BigDecimal("90.0")); } @Test // DATAMONGO-1431, DATAMONGO-2323 diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java index 71c395e822..2628a3a5ca 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java @@ -89,7 +89,9 @@ void setUp() { this.dbRefResolver = spy(new DefaultDbRefResolver(dbFactory)); this.mappingContext = new MongoMappingContext(); this.mappingContext.setSimpleTypeHolder(new MongoCustomConversions(Collections.emptyList()).getSimpleTypeHolder()); + this.mappingContext.afterPropertiesSet(); this.converter = new MappingMongoConverter(dbRefResolver, mappingContext); + this.converter.afterPropertiesSet(); } @Test // DATAMONGO-347 @@ -103,7 +105,7 @@ void createsSimpleDBRefCorrectly() { assertThat(dbRef.getCollectionName()).isEqualTo("person"); } - @Test // DATAMONGO-657 + @Test // DATAMONGO-657, GH-4920 void convertDocumentWithMapDBRef() { Document mapValDocument = new Document(); @@ -610,7 +612,7 @@ void shouldFallbackToOneByOneFetchingWhenElementsInListOfReferencesPointToDiffer verify(converterSpy, never()).bulkReadRefs(anyList()); } - @Test // DATAMONGO-1194 + @Test // DATAMONGO-1194, GH-4920 void shouldBulkFetchMapOfReferences() { MapDBRefVal val1 = new MapDBRefVal(); @@ -642,7 +644,7 @@ void shouldBulkFetchMapOfReferences() { verify(converterSpy, never()).readRef(Mockito.any(DBRef.class)); } - @Test // DATAMONGO-1194 + @Test // DATAMONGO-1194, GH-4920 void shouldBulkFetchLazyMapOfReferences() { MapDBRefVal val1 = new MapDBRefVal(); 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 6f1c7439c0..1f0af2851e 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 @@ -384,7 +384,7 @@ void readsNestedMapsCorrectly() { assertThat(nestedMap.get("afield")).isEqualTo(firstLevel); } - @Test // DATACMNS-42, DATAMONGO-171 + @Test // DATACMNS-42, DATAMONGO-171, GH-4920 void writesClassWithBigDecimal() { BigDecimalContainer container = new BigDecimalContainer(); @@ -394,9 +394,8 @@ void writesClassWithBigDecimal() { org.bson.Document document = new org.bson.Document(); converter.write(container, document); - assertThat(document.get("value")).isInstanceOf(String.class); - assertThat((String) document.get("value")).isEqualTo("2.5"); - assertThat(((org.bson.Document) document.get("map")).get("foo")).isInstanceOf(String.class); + assertThat(document.get("value")).isEqualTo(Decimal128.parse("2.5")); + assertThat(((org.bson.Document) document.get("map")).get("foo")).isInstanceOf(Decimal128.class); } @Test // DATACMNS-42, DATAMONGO-171 @@ -513,7 +512,7 @@ void writesNullValuesForMaps() { assertThat(((org.bson.Document) map).keySet()).contains("en_US"); } - @Test + @Test // GH-4920 void writesBigIntegerIdCorrectly() { ClassWithBigIntegerId foo = new ClassWithBigIntegerId(); @@ -522,7 +521,7 @@ void writesBigIntegerIdCorrectly() { org.bson.Document result = new org.bson.Document(); converter.write(foo, result); - assertThat(result.get("_id")).isInstanceOf(String.class); + assertThat(result.get("_id")).isInstanceOf(Decimal128.class); } @Test diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java index 72d0055389..8de30fd1fe 100755 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java @@ -34,6 +34,7 @@ import org.bson.BsonRegularExpression; import org.bson.conversions.Bson; import org.bson.types.Code; +import org.bson.types.Decimal128; import org.bson.types.ObjectId; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -53,6 +54,7 @@ import org.springframework.data.mongodb.core.aggregation.EvaluationOperators; import org.springframework.data.mongodb.core.aggregation.EvaluationOperators.Expr; import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext; +import org.springframework.data.mongodb.core.convert.MongoCustomConversions.BigDecimalRepresentation; import org.springframework.data.mongodb.core.geo.GeoJsonPoint; import org.springframework.data.mongodb.core.geo.GeoJsonPolygon; import org.springframework.data.mongodb.core.mapping.DBRef; @@ -126,13 +128,30 @@ void convertsStringIntoObjectId() { } @Test - void handlesBigIntegerIdsCorrectly() { + @SuppressWarnings("deprecation") + void handlesBigIntegerIdsCorrectly/*in legacy string format*/() { + + MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, context); + converter.setCustomConversions(MongoCustomConversions.create(adapter -> adapter.bigDecimal(BigDecimalRepresentation.STRING))); + converter.afterPropertiesSet(); + + QueryMapper mapper = new QueryMapper(converter); org.bson.Document document = new org.bson.Document("id", new BigInteger("1")); + org.bson.Document result = mapper.getMappedObject(document, context.getPersistentEntity(IdWrapper.class)); assertThat(result).containsEntry("_id", "1"); } + @Test // GH-4920 + void handlesBigIntegerIdAsDecimal128Correctly() { + + org.bson.Document document = new org.bson.Document("id", new BigInteger("1")); + + org.bson.Document result = mapper.getMappedObject(document, context.getPersistentEntity(IdWrapper.class)); + assertThat(result).containsEntry("_id", Decimal128.parse("1")); + } + @Test void handlesObjectIdCapableBigIntegerIdsCorrectly() { diff --git a/src/main/antora/modules/ROOT/pages/mongodb/mapping/custom-conversions.adoc b/src/main/antora/modules/ROOT/pages/mongodb/mapping/custom-conversions.adoc index 4553be1d43..9c90aafc3d 100644 --- a/src/main/antora/modules/ROOT/pages/mongodb/mapping/custom-conversions.adoc +++ b/src/main/antora/modules/ROOT/pages/mongodb/mapping/custom-conversions.adoc @@ -34,7 +34,7 @@ public class Payment { for details. <2> The desired target type is explicitly defined as `Decimal128` which translates to `NumberDecimal`. Otherwise, the -`BigDecimal` value would have been truned into a `String`. +`BigDecimal` value would have been turned into a `String`. <3> `Date` values are handled by the MongoDB driver itself are stored as `ISODate`. ==== @@ -110,5 +110,12 @@ class MyMongoConfiguration extends AbstractMongoClientConfiguration { MongoDB in its early days did not have support for large numeric values such as `BigDecimal`. To persist `BigDecimal` and `BigInteger` values, Spring Data MongoDB converted values their `String` representation. +This approach had several downsides due to lexical instead of numeric comparison for queries, updates, etc. With MongoDB Server 3.4, `org.bson.types.Decimal128` offers a native representation for `BigDecimal` and `BigInteger`. -You can use the to the native representation by either annotating your properties with `@Field(targetType=DECIMAL128)` or by configuring the big decimal representation in `MongoCustomConversions` through `MongoCustomConversions.create(config -> config.bigDecimal(…))`. +As off Spring Data MongoDB 5.0 the default representation of those types moved to MongoDB native `org.bson.types.Decimal128` as well. +You can still use the to the deprecated `String` variant by configuring the big decimal representation in `MongoCustomConversions` through `MongoCustomConversions.create(config -> config.bigDecimal(BigDecimalRepresentation.STRING))`. + +[NOTE] +==== +Very large values, though being valid in their java, might exceed the maximum bit length of `org.bson.types.Decimal128` in their store native representation. +====