Spring Data MongoDB BigDecimal support

Spring Data MongoDB converts BigDecimal values to String on write and back to when reading. Please have a look at the data mapping and type conversion section of the reference manual.

The default conversion can be modified via @Field(targetType=...) as shown below.

@Field(targetType = DECIMAL128)
private BigDecimal value;

You can change the converter of BigDecimal do Decimal128 which is the java representation of NumberDecimal since mongo 3.4:

@Bean
public MongoCustomConversions mongoCustomConversions() {
    return new MongoCustomConversions(Arrays.asList(

        new Converter<BigDecimal, Decimal128>() {

            @Override
            public Decimal128 convert(@NonNull BigDecimal source) {
                return new Decimal128(source);
            }
        },

        new Converter<Decimal128, BigDecimal>() {

            @Override
            public BigDecimal convert(@NonNull Decimal128 source) {
                return source.bigDecimalValue();
            }

        }


    ));

}

Actually since Spring Data 2.0.7.RELEASE the above changes to following:

@Bean
public MongoCustomConversions mongoCustomConversions() {
    return new MongoCustomConversions(Arrays.asList(
        new BigDecimalDecimal128Converter(),
        new Decimal128BigDecimalConverter()
    ));

}

@WritingConverter
private static class BigDecimalDecimal128Converter implements Converter<BigDecimal, Decimal128> {

    @Override
    public Decimal128 convert(@NonNull BigDecimal source) {
        return new Decimal128(source);
    }
}

@ReadingConverter
private static class Decimal128BigDecimalConverter implements Converter<Decimal128, BigDecimal> {

    @Override
    public BigDecimal convert(@NonNull Decimal128 source) {
        return source.bigDecimalValue();
    }

}

As is documented at the Spring Data MongoDB - Reference Documentation 18.6. Custom Conversions - Overriding Default Mapping:

The most trivial way of influencing the mapping result is by specifying the desired native MongoDB target type via the @Field annotation. This allows to work with non MongoDB types like BigDecimal in the domain model while persisting values in native org.bson.types.Decimal128 format.

public class Payment {

  @Field(targetType = FieldType.DECIMAL128) 
  BigDecimal value;

}

The desired target type is explicitly defined as Decimal128 which translates to NumberDecimal. Otherwise the BigDecimal value would have been turned into a String:

{
  "value" : NumberDecimal(2.099)
}

I had a corner case where I had a lot of decimals and Decimal128`s constructor raised the following exception:

Failed to convert from type [java.math.BigDecimal] to type [org.bson.types.Decimal128] for value '21.6000000000000000888178419700125232338905334472656250'; nested exception is java.lang.NumberFormatException: Conversion to Decimal128 would require inexact rounding of 21.6000000000000000888178419700125232338905334472656250

The Solution to this was to round down the input:

new Decimal128(source.round(MathContext.DECIMAL128))