Custom Spring Data MongoDB Repository Methods

Integrate Spring Data provided functionality with manually implemented methods in a single MongoDB repository.

The Interface

We define an interface for the functionality that is to be implemented manually.

MedalsRepositoryCustom.java
package io.lishman.springdata.repository;

public interface MedalsRepositoryCustom {
    
    public int getBronzeCount(String countryName);

    public int getSilverCount(String countryName);
    
    public int getGoldCount(String countryName);

}

The Implementation

Then we provide an implementation of this interface for our manually defined methods.

Note that neither the interface nor the class knows anything about Spring Data.

However, the name of this class is important as we will see next.

MedalsRepositoryImpl.java
package io.lishman.springdata.repository;

import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoOperations;

import io.lishman.springdata.domain.OlympicMedals;
import io.lishman.springdata.domain.OlympicMedals.MedalType;

/* 
 * Spring will find this class because the name follows a convention of 
 * 
 *      <InterfaceName>Impl
 * 
 * An alternative would be to specify 
 * 
 *      @Repository("medalsRepositoryImpl")
 *      
 * then the class could be called anything.
 *  
 * However, in this case it would need to be picked up by a component scan.
 * 
 *      @ComponentScan(basePackageClasses=RepositoryPackage.class)
 */
public class MedalsRepositoryImpl implements MedalsRepositoryCustom {
    
    @Autowired private MongoOperations operations;

    @Override
    public int getBronzeCount(String countryName) {
        return getMedalsForCountry(countryName).getMedalCount(MedalType.BRONZE);
    }

    @Override
    public int getSilverCount(String countryName) {
        return getMedalsForCountry(countryName).getMedalCount(MedalType.SILVER);
    }

    @Override
    public int getGoldCount(String countryName) {
        return getMedalsForCountry(countryName).getMedalCount(MedalType.GOLD);
    }

    private OlympicMedals getMedalsForCountry(String countryName) {
        return operations.findOne(query(where("countryName").is(countryName)), OlympicMedals.class);
    }
}

The Respository

Now we define the repository by extending the manually implemented interface and the Repository interface.

Spring will use the name of this interface to search for a class called MedalsRepositoryImpl (the class we saw in the previous step), and integrate the manually implemented methods with any query methods included on this interface.

MedalsRepository.java
package io.lishman.springdata.repository;

import io.lishman.springdata.domain.OlympicMedals;
import org.springframework.data.repository.Repository;

import java.math.BigInteger;

public interface MedalsRepository extends Repository<OlympicMedals, BigInteger>,
                                          MedalsRepositoryCustom {
    
    public OlympicMedals findByCountryName(String name);

}