Object Mapping with Spring Data Mongodb

Use @Document, @Field, @Id, @DBRef, @PersistenceConstructor, @TypeAlias and @Indexed to map Java objects to MongoDB documents.

Example Details

The examples demonstrate how to use:

  • @Document to identify a domain object to be persisted to MongoDB.
  • @Field to name the key to be used for the field in the document.
  • @Id to specify the document identifier.
  • @DBRef to store a pointer to a document rather than embedding the document itself.
  • @TypeAlias to replace the fully qualified class name with a different value.
  • @PersistenceConstructor to nominate a constructor to be used when creating an object from the database.
  • @Indexed to choose which fields are to be indexed and whether or not duplicate values are allowed.

Object Mapping

The Spring Data MongoDB mapping subsystem takes a domain class such as this, and converts it into a MongoDB DBObject.

By default a field with a name of 'id' or '_id' will be used as the ID field of the document.

Continent.java
package io.lishman.springdata.domain;


public final class Continent {
    
    private final long id;
    private final String name;
    
    public Continent(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }
    
    public String toString() {
        return this.getName();
    }
}

The @Id Annotation

@Id explicitly specifies the document identifier and is useful when the property name is something other than the default.

We have included this in a base class to be extended by our top-level documents.

AbstractDocument.java
package io.lishman.springdata.domain;

import java.math.BigInteger;

import org.springframework.data.annotation.Id;

public class AbstractDocument {

    @Id private BigInteger documentId;

    public void setId(BigInteger id) {
        this.documentId = id;
    }

    public BigInteger getId() {
        return documentId;
    }

}

@Document and @Field

@Document identifies a domain object to be persisted to MongoDB.

collection is the name of the database collection that this object is mapped to. Without it, a default name of 'olympicMedals' would be derived from the class name.

This class extends the AbstractDocument we saw earlier to include the id field.

@Field gives a name to the key to be used to store the field inside the document.

The property name (i.e. 'countryName') would be used as the field key if this annotation was not included.

Remember that field keys are repeated for every document so using a smaller key name will reduce the required space.

OlympicMedals.java
package io.lishman.springdata.domain;

import java.util.ArrayList;
import java.util.List;

import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

@Document(collection="medals")
@TypeAlias("medal")
public class OlympicMedals extends AbstractDocument {
    
    public enum MedalType {BRONZE, SILVER, GOLD}
    
    @Field("name")
    private String countryName;

    private List<Integer> medals = new ArrayList<Integer>();


    public OlympicMedals(String countryName, int gold, int silver, int bronze) {
        this.setCountryName(countryName);
        medals.add(bronze);
        medals.add(silver);
        medals.add(gold);
    }
    
    @PersistenceConstructor
    public OlympicMedals(String countryName, List<Integer> medals) {
        this.setCountryName(countryName);
        this.medals = medals;
    }

    public void setCountryName(String countryName) {
        this.countryName = countryName;
    }

    public String getCountryName() {
        return countryName;
    }
    
    public int getMedalCount(MedalType type) {
        return medals.get(type.ordinal());
    }
    
}

@DBRef and @TypeAlias

@DBRef stores a reference, or pointer, to a document, rather than embedding it directly in the parent document.

This is similar to a foreign key in a relational database.

Without this annotation the Country object would end up as a nested document inside the City document.

By default, Spring Data MongoDB stores the fully qualified class name in each document like this:

{ _class : "com.lishman.springdata.domain.City", 
   name : "New York" 
}

@TypeAlias replaces the full path name with a value of our choice.

{ _class : "city", name : "New York" }
City.java
package io.lishman.springdata.domain;

import java.util.List;

import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection="cities")
@TypeAlias("city")
public class City extends AbstractDocument {

    private String name;
    private List<String> attractions;
   
    @DBRef
    private Country country;

    public City(String name, final Country country, List<String> attractions) {
        this.setName(name);
        this.setCountry(country);
        this.setAttractions(attractions);
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    
    public void setAttractions(List<String> attractions) {
        this.attractions = attractions;
    }
    
    public List<String> getAttractions() {
        return attractions;
    }
    
    public void setCountry(Country country) {
        this.country = country;
    }
    
    public Country getCountry() {
        return country;
    }
    
    public String toString() {
        return getName();
    }
}

@PersistenceConstructor and @Indexed

@PersistenceConstructor marks a constructor to use when instantiating the object from the database.

Constructor arguments are mapped by name to the key values in the retrieved DBObject.

The @Indexed annotation identifies which fields are to be indexed.

unique=true will throw a DuplicateKeyException if an attempt is made to insert a duplicate value in the field.

@CompoundIndex can be used to define indexes with multiple properties.

Ocean.java
package io.lishman.springdata.domain;

import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection="oceans")
@TypeAlias("ocean")
public class Ocean extends AbstractDocument {

    @Indexed(name="oceanName", unique=true)
    private String name;
    private int area;
    
    @PersistenceConstructor
    public Ocean(String name, int area) {
        setName(name);
        setArea(area);
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    
    public void setArea(int area) {
        this.area = area;
    }
    
    public int getarea() {
        return area;
    }
    
    public String toString() {
        return getName();
    }
}