Custom converter for Mongodb and Spring Data for BigDecimal type

Recently I found that MongoDB does not support java's java.math.BigDecimal as you can read in the official spring data mongodb documentation

As it turns out java.math.BigDecimal will be converted to java.lang.String when saved to the database and parsed back to BigDecimal which in most cases I guess it's alright

However as I found out things get broken when trying to perform range queries on this field such as : $gte, $lte you will get inconsistent results since MongoDB sees this as a String

Luckily this can be easily fixed by creating 2 custom converters (one for each side), below is how I've done it :

1. Define custom converters by implementing the appropriate interface

import org.springframework.core.convert.converter.Converter;

import java.math.BigDecimal;

public class BigDecimalToDoubleConverter implements Converter<BigDecimal, Double> {

    @Override
    public Double convert(BigDecimal source) {
        return source.doubleValue();
    }
}

import org.springframework.core.convert.converter.Converter;

import java.math.BigDecimal;


public class DoubleToBigDecimalConverter implements Converter<Double, BigDecimal> {

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

2. Configure the MongoTemplate to use the custom converters



@Configuration
public class MongoDBConfig  {

   
     //MongoDB properties read from the application.yaml configuration file (to handle different profiles)
    @Value("${spring.data.mongodb.host}")
    private String mongoHost;

    @Value("${spring.data.mongodb.port}")
    private int mongoPort;

    @Value("${spring.data.mongodb.database}")
    private String mongoDatabase;

   
    @Bean
    public MongoTemplate mongoTemplate() throws Exception {

        MongoTemplate mongoTemplate = new MongoTemplate(mongo(), mongoDatabase);
        MappingMongoConverter mongoMapping = (MappingMongoConverter) mongoTemplate.getConverter();
        mongoMapping.setCustomConversions(customConversions()); // tell mongodb to use the custom converters
        mongoMapping.afterPropertiesSet();
        return mongoTemplate;

    }

    /**
    * Configure the MongoDB client
    * 
    **/
    @Bean
    public Mongo mongo() throws Exception {
        return new MongoClient(mongoHost, mongoPort);
    }


   /**
    * Returns the list of custom converters that will be used by the MongoDB template
    * 
    **/
    public CustomConversions customConversions() {
        return new CustomConversions(Arrays.asList(new DoubleToBigDecimalConverter(), new BigDecimalToDoubleConverter()));
    }
}


Please note that this is OK in most cases however please note that in some cases this conversion from BigDecimal to Double may lead to issues; more info : Oracle's official doc on floating point arithmetic

2 comments:

  1. In the newer Spring Data they added simpler way to support BigDecimal:

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

    https://docs.spring.io/spring-data/data-mongodb/docs/current/reference/html/#mongo.custom-converters

    ReplyDelete
  2. Any suggestion for same issue in MySQL?

    ReplyDelete