Skip to main content
unbound mongodb at scale

The Mapping Tax: Spring Data MongoDB Overhead

2 min read Chapter 13 of 72

The Mapping Tax: Spring Data MongoDB Overhead

Spring Data MongoDB provides an abstraction layer over the MongoDB Java Sync Driver. That abstraction has a cost. Every document read through Spring Data passes through MappingMongoConverter, which uses reflection, type inspection, and property population to convert BSON documents into Java objects. Every document written goes through the reverse path. This conversion layer adds CPU time, memory allocation, and GC pressure that does not exist when using the raw driver.

The question is not whether the mapping tax exists. It does. The question is whether the productivity gains justify the performance cost for your specific workload.

Spring Data mapping tax pipeline showing Document to Entity conversion stages with JMH timing at each stage: BSON decode 0.8us, Property discovery 1.2us, Type conversion 0.6us, Population 0.4us. Total mapping overhead: 3.0us per document

The Mapping Pipeline

When Spring Data reads a document, it passes through five stages:

  1. BSON Decode: The raw wire protocol bytes are decoded into a BsonDocument. This happens in the driver, not in Spring Data.
  2. Property Discovery: MongoPersistentEntity uses reflection to discover the target class’s fields, annotations, and type information. This is cached after the first call, but the cache lookup still costs cycles.
  3. Type Inspection: Each field is inspected for type compatibility. A BSON Int64 mapped to a Java long is cheap. A BSON Document mapped to an embedded @Document class triggers recursive mapping.
  4. Conversion: Values are converted using the registered Converter instances. Custom converters add their own overhead.
  5. Population: The Java object is instantiated and fields are set. Spring Data uses property accessors generated via ClassGeneratingPropertyAccessorFactory, which avoids raw reflection but still has overhead compared to direct constructor calls.

For the telemetry platform’s TelemetryReading entity with 8 fields, this pipeline adds 3.0 microseconds per document. At 100 documents per query and 1,000 queries per second, that is 300ms of cumulative CPU time per second spent on mapping.

// Spring Data entity definition
@Document(collection = "readings")
public class TelemetryReading {
    @Id
    private String id;
    private String sensorId;
    private Instant timestamp;
    private double temperature;
    private double humidity;
    private double pressure;
    private GeoJsonPoint location;
    @Version
    private Long version;
}

The Instant field requires BSON DateTime to java.time.Instant conversion. The GeoJsonPoint field triggers a nested document mapping. The @Version field adds optimistic locking overhead on writes.