We are the Dev Teams of
  • brands
  • ebay_main
  • ebay
  • mobile
<
>
BLOG

Mapping BigDecimals with Morphia for MongoDB

by Benjamin Eckart
in Tutorials

It's generally better to use BigDecimals for storing prices, because floats and doubles do not store exact values. Unfortunately, if you use Morphia as an abstraction layer for MongoDB, BigDecimals are not supported.

To overcome this issue, you can convert those BigDecimals to exact data types, such as long values.

The following class will convert BigDecimals to long values.

The following class will store big decimals as two values: an unscaled value and a scale.

For example, the value 55.5 will be stored as "unscaled": 555 and "scale": 1. 

public class BigDecimalConverter extends TypeConverter implements SimpleValueConverter {

    public BigDecimalConverter() {
        super(BigDecimal.class);
    }

    @Override
    public Object encode(Object value, MappedField optionalExtraInfo) {
        if (value == null) {
            return null;
        }
        BigDecimal bigDecimalValue = (BigDecimal) value;

        if (bigDecimalValue.scale() > 18) {
            bigDecimalValue = bigDecimalValue.setScale(18, BigDecimal.ROUND_DOWN);
        }

        DBObject dbo = new BasicDBObject();

        dbo.put("unscaled", bigDecimalValue.unscaledValue().longValue());
        dbo.put("scale", bigDecimalValue.scale());

        return dbo;
    }

    @Override
    @SuppressWarnings("rawtypes")
    public Object decode(Class targetClass, Object fromDBObject, MappedField field) throws MappingException {
        DBObject dbo = (DBObject) fromDBObject;
        if (dbo == null) {
            return null;
        }

        BigDecimal bigDecimal = null;

        Long unscaled = (Long) dbo.get("unscaled");
        Integer scale = (Integer) dbo.get("scale");

        if (unscaled != null && scale != null) {
            bigDecimal = new BigDecimal(new BigInteger(unscaled.toString()), scale);
        }

        return bigDecimal;
    }

}

The BigDecimalConverter extends the TypeConverter class. If the value to be stored has more than 18 fraction digits, the value will be rounded.

The converter needs to be registered. You can do this using the following code:

Morphia morphia = new Morphia();
morphia.getMapper().getConverters().addConverter(BigDecimalConverter.class);

Disclaimer: BigDecimal can store larger numbers than this converter will be able to convert. For most use cases, the presented implementation should be sufficient. If you need to store really large numbers, you could convert the scaled and unscaled value into multiple longs.

Morphia, BigDecimals, MongoDB

?>