The Mapping Tax: Spring Data MongoDB Overhead
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.
The Mapping Pipeline
When Spring Data reads a document, it passes through five stages:
- BSON Decode: The raw wire protocol bytes are decoded into a
BsonDocument. This happens in the driver, not in Spring Data. - Property Discovery:
MongoPersistentEntityuses 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. - Type Inspection: Each field is inspected for type compatibility. A BSON
Int64mapped to a Javalongis cheap. A BSONDocumentmapped to an embedded@Documentclass triggers recursive mapping. - Conversion: Values are converted using the registered
Converterinstances. Custom converters add their own overhead. - 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.