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))