Skip to content

Commit 1c3da7f

Browse files
mp911dechristophstrobl
authored andcommitted
Polishing.
Register BigDecimal and BigInteger as simple types to allow explicitly typed writes and register converters in the ConversionService directly to avoid forcing a specific type. Revert BigDecimalRepresentation list change to use only one representation. In a sense, we're aligning with MongoDB's driver behavior that BigDecimal now maps by default to Decimal128, while BigInteger requires explicit configuration. Original Pull Request: #5051
1 parent 8b80789 commit 1c3da7f

File tree

5 files changed

+216
-80
lines changed

5 files changed

+216
-80
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,7 @@ private void writeSimpleInternal(@Nullable Object value, Bson bson, MongoPersist
13761376

13771377
if (typeHint != null && Object.class != typeHint) {
13781378

1379+
// TODO this is weird and leads to double-conversion in some cases, e.g. BigDecimal -> Decimal128 -> BigDecimal
13791380
if (conversionService.canConvert(value.getClass(), typeHint)) {
13801381
value = doConvert(value, typeHint);
13811382
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ static Collection<Object> getDateToUtcConverters() {
110110
* @return
111111
* @since 5.0
112112
*/
113-
static Collection<Object> getBigNumberDecimal128Converters() {
113+
static Collection<Converter<?, ?>> getBigNumberDecimal128Converters() {
114114

115-
List<Object> converters = new ArrayList<>(3);
115+
List<Converter<?, ?>> converters = new ArrayList<>(3);
116116

117117
converters.add(BigDecimalToDecimal128Converter.INSTANCE);
118118
converters.add(Decimal128ToBigDecimalConverter.INSTANCE);
@@ -127,9 +127,9 @@ static Collection<Object> getBigNumberDecimal128Converters() {
127127
* @return
128128
* @since 5.0
129129
*/
130-
static Collection<Object> getBigNumberStringConverters() {
130+
static Collection<Converter<?, ?>> getBigNumberStringConverters() {
131131

132-
List<Object> converters = new ArrayList<>(4);
132+
List<Converter<?, ?>> converters = new ArrayList<>(2);
133133

134134
converters.add(BigDecimalToStringConverter.INSTANCE);
135135
converters.add(BigIntegerToStringConverter.INSTANCE);
@@ -228,6 +228,7 @@ public ObjectId convert(BigInteger source) {
228228
}
229229
}
230230

231+
@WritingConverter
231232
enum BigDecimalToStringConverter implements Converter<BigDecimal, String> {
232233
INSTANCE;
233234

@@ -239,6 +240,7 @@ public String convert(BigDecimal source) {
239240
/**
240241
* @since 2.2
241242
*/
243+
@WritingConverter
242244
enum BigDecimalToDecimal128Converter implements Converter<BigDecimal, Decimal128> {
243245
INSTANCE;
244246

@@ -250,6 +252,7 @@ public Decimal128 convert(BigDecimal source) {
250252
/**
251253
* @since 5.0
252254
*/
255+
@WritingConverter
253256
enum BigIntegerToDecimal128Converter implements Converter<BigInteger, Decimal128> {
254257
INSTANCE;
255258

@@ -258,6 +261,7 @@ public Decimal128 convert(BigInteger source) {
258261
}
259262
}
260263

264+
@ReadingConverter
261265
enum StringToBigDecimalConverter implements Converter<String, BigDecimal> {
262266
INSTANCE;
263267

@@ -269,6 +273,7 @@ enum StringToBigDecimalConverter implements Converter<String, BigDecimal> {
269273
/**
270274
* @since 2.2
271275
*/
276+
@ReadingConverter
272277
enum Decimal128ToBigDecimalConverter implements Converter<Decimal128, BigDecimal> {
273278
INSTANCE;
274279

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,16 @@
3030
import java.util.Locale;
3131
import java.util.Set;
3232
import java.util.function.Consumer;
33+
import java.util.function.Predicate;
3334

3435
import org.apache.commons.logging.Log;
3536
import org.apache.commons.logging.LogFactory;
3637
import org.jspecify.annotations.Nullable;
38+
3739
import org.springframework.core.convert.TypeDescriptor;
3840
import org.springframework.core.convert.converter.Converter;
3941
import org.springframework.core.convert.converter.ConverterFactory;
42+
import org.springframework.core.convert.converter.ConverterRegistry;
4043
import org.springframework.core.convert.converter.GenericConverter;
4144
import org.springframework.data.convert.ConverterBuilder;
4245
import org.springframework.data.convert.PropertyValueConversions;
@@ -78,6 +81,12 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus
7881
STORE_CONVERTERS = Collections.unmodifiableList(converters);
7982
}
8083

84+
/**
85+
* Converters to be registered with the {@code ConversionService} but hidden from CustomConversions to avoid
86+
* converter-based type hinting.
87+
*/
88+
private final List<Converter<?, ?>> fallbackConversionServiceConverters = new ArrayList<>();
89+
8190
/**
8291
* Creates an empty {@link MongoCustomConversions} object.
8392
*/
@@ -101,7 +110,12 @@ public MongoCustomConversions(List<?> converters) {
101110
* @since 2.3
102111
*/
103112
protected MongoCustomConversions(MongoConverterConfigurationAdapter conversionConfiguration) {
104-
super(conversionConfiguration.createConverterConfiguration());
113+
this(conversionConfiguration.createConverterConfiguration());
114+
}
115+
116+
private MongoCustomConversions(MongoConverterConfiguration converterConfiguration) {
117+
super(converterConfiguration);
118+
this.fallbackConversionServiceConverters.addAll(converterConfiguration.fallbackConversionServiceConverters);
105119
}
106120

107121
/**
@@ -120,6 +134,12 @@ public static MongoCustomConversions create(Consumer<MongoConverterConfiguration
120134
return new MongoCustomConversions(adapter);
121135
}
122136

137+
@Override
138+
public void registerConvertersIn(ConverterRegistry conversionService) {
139+
this.fallbackConversionServiceConverters.forEach(conversionService::addConverter);
140+
super.registerConvertersIn(conversionService);
141+
}
142+
123143
@WritingConverter
124144
private enum CustomToStringConverter implements GenericConverter {
125145

@@ -155,7 +175,7 @@ public static class MongoConverterConfigurationAdapter {
155175
LocalDateTime.class);
156176

157177
private boolean useNativeDriverJavaTimeCodecs = false;
158-
private BigDecimalRepresentation @Nullable [] bigDecimals;
178+
private @Nullable BigDecimalRepresentation bigDecimals;
159179
private final List<Object> customConverters = new ArrayList<>();
160180

161181
private final PropertyValueConversions internalValueConversion = PropertyValueConversions.simple(it -> {});
@@ -312,14 +332,14 @@ public MongoConverterConfigurationAdapter useSpringDataJavaTimeCodecs() {
312332
* Configures the representation to for {@link java.math.BigDecimal} and {@link java.math.BigInteger} values in
313333
* MongoDB. Defaults to {@link BigDecimalRepresentation#DECIMAL128}.
314334
*
315-
* @param representations ordered list of representations to use (first one is default)
335+
* @param representation the representation to use.
316336
* @return this.
317337
* @since 4.5
318338
*/
319-
public MongoConverterConfigurationAdapter bigDecimal(BigDecimalRepresentation... representations) {
339+
public MongoConverterConfigurationAdapter bigDecimal(BigDecimalRepresentation representation) {
320340

321-
Assert.notEmpty(representations, "BigDecimalDataType must not be null");
322-
this.bigDecimals = representations;
341+
Assert.notNull(representation, "BigDecimalDataType must not be null");
342+
this.bigDecimals = representation;
323343
return this;
324344
}
325345

@@ -365,27 +385,32 @@ PropertyValueConversions valueConversions() {
365385
return this.propertyValueConversions;
366386
}
367387

368-
ConverterConfiguration createConverterConfiguration() {
388+
MongoConverterConfiguration createConverterConfiguration() {
369389

370390
if (hasDefaultPropertyValueConversions()
371391
&& propertyValueConversions instanceof SimplePropertyValueConversions svc) {
372392
svc.init();
373393
}
374394

375395
List<Object> storeConverters = new ArrayList<>(STORE_CONVERTERS.size() + 10);
376-
377-
if (bigDecimals != null) {
378-
for (BigDecimalRepresentation representation : bigDecimals) {
379-
switch (representation) {
380-
case STRING -> storeConverters.addAll(MongoConverters.getBigNumberStringConverters());
381-
case DECIMAL128 -> storeConverters.addAll(MongoConverters.getBigNumberDecimal128Converters());
382-
}
396+
List<Converter<?, ?>> fallbackConversionServiceConverters = new ArrayList<>(5);
397+
fallbackConversionServiceConverters.addAll(MongoConverters.getBigNumberStringConverters());
398+
fallbackConversionServiceConverters.addAll(MongoConverters.getBigNumberDecimal128Converters());
399+
400+
if (bigDecimals == null) {
401+
if (LOGGER.isInfoEnabled()) {
402+
LOGGER.info(
403+
"No BigDecimal/BigInteger representation set. Choose 'BigDecimalRepresentation.DECIMAL128' or 'BigDecimalRepresentation.String' to store values in desired format.");
404+
}
405+
} else {
406+
switch (bigDecimals) {
407+
case STRING -> storeConverters.addAll(MongoConverters.getBigNumberStringConverters());
408+
case DECIMAL128 -> storeConverters.addAll(MongoConverters.getBigNumberDecimal128Converters());
383409
}
384-
} else if (LOGGER.isInfoEnabled()) {
385-
LOGGER.info(
386-
"No BigDecimal/BigInteger representation set. Choose [DECIMAL128] and/or [String] to store values in desired format.");
387410
}
388411

412+
fallbackConversionServiceConverters.removeAll(storeConverters);
413+
389414
if (useNativeDriverJavaTimeCodecs) {
390415

391416
/*
@@ -397,7 +422,8 @@ ConverterConfiguration createConverterConfiguration() {
397422
StoreConversions storeConversions = StoreConversions
398423
.of(new SimpleTypeHolder(JAVA_DRIVER_TIME_SIMPLE_TYPES, MongoSimpleTypes.HOLDER), storeConverters);
399424

400-
return new ConverterConfiguration(storeConversions, this.customConverters, convertiblePair -> {
425+
return new MongoConverterConfiguration(storeConversions, fallbackConversionServiceConverters,
426+
this.customConverters, convertiblePair -> {
401427

402428
// Avoid default registrations
403429

@@ -408,8 +434,10 @@ ConverterConfiguration createConverterConfiguration() {
408434
}
409435

410436
storeConverters.addAll(STORE_CONVERTERS);
411-
return new ConverterConfiguration(StoreConversions.of(MongoSimpleTypes.createSimpleTypeHolder(), storeConverters),
412-
this.customConverters, convertiblePair -> true, this.propertyValueConversions);
437+
return new MongoConverterConfiguration(
438+
StoreConversions.of(MongoSimpleTypes.createSimpleTypeHolder(), storeConverters),
439+
fallbackConversionServiceConverters, this.customConverters, convertiblePair -> true,
440+
this.propertyValueConversions);
413441
}
414442

415443
private boolean hasDefaultPropertyValueConversions() {
@@ -418,6 +446,19 @@ private boolean hasDefaultPropertyValueConversions() {
418446

419447
}
420448

449+
static class MongoConverterConfiguration extends ConverterConfiguration {
450+
451+
private final List<Converter<?, ?>> fallbackConversionServiceConverters;
452+
453+
public MongoConverterConfiguration(StoreConversions storeConversions,
454+
List<Converter<?, ?>> fallbackConversionServiceConverters, List<?> userConverters,
455+
Predicate<GenericConverter.ConvertiblePair> converterRegistrationFilter,
456+
@Nullable PropertyValueConversions propertyValueConversions) {
457+
super(storeConversions, userConverters, converterRegistrationFilter, propertyValueConversions);
458+
this.fallbackConversionServiceConverters = fallbackConversionServiceConverters;
459+
}
460+
}
461+
421462
/**
422463
* Strategy to represent {@link java.math.BigDecimal} and {@link java.math.BigInteger} values in MongoDB.
423464
*

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.mongodb.core.mapping;
1717

18+
import java.math.BigDecimal;
1819
import java.math.BigInteger;
1920
import java.time.Instant;
2021
import java.util.Set;
@@ -60,7 +61,7 @@ public abstract class MongoSimpleTypes {
6061
BsonDocument.class, BsonDouble.class, BsonInt32.class, BsonInt64.class, BsonJavaScript.class,
6162
BsonJavaScriptWithScope.class, BsonObjectId.class, BsonRegularExpression.class, BsonString.class,
6263
BsonTimestamp.class, Geometry.class, GeometryCollection.class, LineString.class, MultiLineString.class,
63-
MultiPoint.class, MultiPolygon.class, Point.class, Polygon.class);
64+
MultiPoint.class, MultiPolygon.class, Point.class, Polygon.class, BigInteger.class, BigDecimal.class);
6465

6566
public static final SimpleTypeHolder HOLDER = createSimpleTypeHolder();
6667

0 commit comments

Comments
 (0)