1. Introducción

En el siguiente post veremos como conectarnos a una instancia de MongoDB a través de Spring Data. Para ello necesitaremos:

  • Añadir dependencias Spring Data
  • Configurar conexión

Si quieres ver como instalar MongoDB en local y saber un poco más ir a Primeros pasos en MongoDB

Os podéis descargar el código de ejemplo de mi GitHub aquí.

Tecnologías empleadas:

  • Java 8
  • Gradle 3.1
  • Spring-Test 4.3.7.RELEASE
  • SpringData-MongoDB 1.10.1.RELEASE

2. Colección y documentos

La colección y documentos que vamos a utilizar son los que se han creado e insertado en el post Primeros pasos en MongoDB

Colección
User

Documentos

db.user.insert({"_id":"1","name":"admin","surname":"admin","gender":"male","roles":["ROLE_ADMIN"]})
db.user.insert({"_id":"2","name":"Jorge","surname":"HernándezRamírez","gender":"male","roles":["ROLE_ADMIN"],"teams":[{"name":"UD.LasPalmas","sport":"Football"},{"name":"RealMadrid","sport":"Football"},{"name":"McLaren","sport":"F1"}]})
db.user.insert({"_id":"3","name":"Jose","gender":"male","surname":"HernándezRamírez","roles":["ROLE_USER"],"teams":[{"name":"UD.LasPalmas","sport":"Football"},{"name":"MagnusCarlsen","sport":"Chess"}]})
db.user.insert({"_id":"4","name":"Raul","surname":"GonzálezBlanco","gender":"male","roles":["ROLE_USER"],"teams":[{"name":"RealMadrid","sport":"Football"},{"name":"RealMadrid","sport":"Basketball"}]})
db.user.insert({"_id":"5","name":"Constanza","surname":"RamírezRodríguez","gender":"female","roles":["ROLE_USER"],"teams":[{"name":"UD.LasPalmas","sport":"Football"}]})

3. Ficheros

Dependencias
Añadimos la dependencia spring-data-mongodb

group 'com.jorgehernandezramirez.spring.springdata'

version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'org.springframework.data', name: 'spring-data-mongodb', version: spring_data_mongodb_version
    testCompile group: 'org.springframework', name: 'spring-test', version: spring_test_version
    testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.2'
    testCompile group: 'junit', name: 'junit', version: junit_version
}

Configuración
Se muestra la configuración para conectar con nuestra base de datos que se encuentra en localhost:27017.

package com.jorgehernandezramirez.spring.springdata.mongodb.configuration;

import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

@Configuration
@EnableMongoRepositories(basePackages = "com.jorgehernandezramirez.spring.springdata.mongodb.repository")
public class MongoDBConfiguration extends AbstractMongoConfiguration {

    @Override
    public MongoMappingContext mongoMappingContext()
            throws ClassNotFoundException {
        return super.mongoMappingContext();
    }

    @Override
    @Bean
    public Mongo mongo() throws Exception {
        return new MongoClient("localhost" + ":" + "27017");
    }

    @Override
    protected String getDatabaseName() {
        return "development";
    }
}

Se muestra el repositorio y la entidad asociada a la colección user

package com.jorgehernandezramirez.spring.springdata.mongodb.repository;

import com.jorgehernandezramirez.spring.springdata.mongodb.entity.UserEntity;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;

import java.util.List;

/**
 * Repository de la entidad User
 */
public interface UserRepository extends MongoRepository<UserEntity, String> {

    List<UserEntity> findByName(String name);

    @Query("{'teams.name': ?0}")
    List<UserEntity> findByTeamname(String teamName);
}
package com.jorgehernandezramirez.spring.springdata.mongodb.entity;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;

@Document(collection = "user")
public class UserEntity {

    @Id
    private String id;

    private String name;

    private String surname;

    private String gender;

    private List<String> roles;

    private List<Team> teams;

    public UserEntity(){
        //For Spring Data
    }

    public UserEntity(String id, String name, String surname, String gender, List<String> roles, List<Team> teams) {
        this.id = id;
        this.name = name;
        this.surname = surname;
        this.gender = gender;
        this.roles = roles;
        this.teams = teams;
    }


    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public List<String> getRoles() {
        return roles;
    }

    public void setRoles(List<String> roles) {
        this.roles = roles;
    }

    public List<Team> getTeams() {
        return teams;
    }

    public void setTeams(List<Team> teams) {
        this.teams = teams;
    }

    @Override
    public String toString() {
        return "UserEntity{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", surname='" + surname + '\'' +
                ", gender='" + gender + '\'' +
                ", roles=" + roles +
                ", teams=" + teams +
                '}';
    }
}
package com.jorgehernandezramirez.spring.springdata.mongodb.entity;

public class Team {

    private String name;

    private String sport;

    public Team(){
        //For Spring Data
    }

    public Team(String name, String sport) {
        this.name = name;
        this.sport = sport;
    }

    public String getName() {
        return name;
    }

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

    public String getSport() {
        return sport;
    }

    public void setSport(String sport) {
        this.sport = sport;
    }

    @Override
    public String toString() {
        return "Team{" +
                "name='" + name + '\'' +
                ", sport='" + sport + '\'' +
                '}';
    }
}

4. Testeando nuestro repositorio

Test

package com.jorgehernandezramirez.spring.springdata.mongodb;

import com.jorgehernandezramirez.spring.springdata.mongodb.configuration.MongoDBConfiguration;
import com.jorgehernandezramirez.spring.springdata.mongodb.entity.Count;
import com.jorgehernandezramirez.spring.springdata.mongodb.entity.UserEntity;
import com.jorgehernandezramirez.spring.springdata.mongodb.entity.UserGenderCount;
import com.jorgehernandezramirez.spring.springdata.mongodb.repository.UserRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;

import static org.junit.Assert.assertNotNull;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MongoDBConfiguration.class,loader=AnnotationConfigContextLoader.class)
public class MongoDBTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(MongoDBTest.class);

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private UserRepository userRepository;

    @Test
    public void shouldBeNotNullMongoTemplate(){
        assertNotNull(mongoTemplate);
        assertNotNull(userRepository);
    }

    @Test
    public void shouldReturnAllUsersFromDB(){
        userRepository.findAll().forEach(userEntity -> {
            LOGGER.info("{}", userEntity);
        });
    }

    @Test
    public void shouldReturnAllUsersFromUsername(){
        userRepository.findByName("Jorge").forEach(userEntity -> {
            LOGGER.info("{}", userEntity);
        });
    }

    @Test
    public void shouldReturnAllUsersLikeUDLasPalmas(){
        userRepository.findByTeamname("UD. Las Palmas").forEach(userEntity -> {
            LOGGER.info("{}", userEntity);
        });
    }

Utilizando agregaciones

Obtener el número de chicos y chicas en el sistema

Análogo a la siguiente query

db.user.aggregate(
   {$group: {_id: "$gender", count : {$sum : 1}}}
)
    @Test
    public void shouldGetNumberOfMenAndWomens(){
        final Aggregation aggregation = newAggregation(
                group("gender").count().as("count"),
                project("count").and("gender").previousOperation());
        final AggregationResults<UserGenderCount> groupResults
                = mongoTemplate.aggregate(aggregation, UserEntity.class, UserGenderCount.class);
        LOGGER.info("{}", groupResults.getMappedResults());
    }

Se mapean los datos sobre UserGenderCount

package com.jorgehernandezramirez.spring.springdata.mongodb.entity;

public class UserGenderCount {

    private String gender;

    private Integer count;

    public UserGenderCount(){
        //For Spring Data
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    @Override
    public String toString() {
        return "UserGenderCount{" +
                "gender='" + gender + '\'' +
                ", count=" + count +
                '}';
    }
}

Obtener el número de veces que el Real Madrid es un equipo favorito de un usuario

Análogo a la siguiente query

 db.user.aggregate(
   {$unwind: "$teams"},
   {$match: {"teams.name": "Real Madrid"}},
   {$group: {_id: null, count: {$sum: 1}}}
)
    @Test
    public void shouldGetNumberRealMadridLikers(){
        final Aggregation aggregation = newAggregation(
                unwind("teams"),
                match(Criteria.where("teams.name").is("Real Madrid")),
                group().count().as("count"),
                project("count"));
        final AggregationResults<Count> groupResults
                = mongoTemplate.aggregate(aggregation, UserEntity.class, Count.class);
        LOGGER.info("{}", groupResults.getMappedResults());
    }

Se muestra la clase sobre la que se mapeará el resultado

package com.jorgehernandezramirez.spring.springdata.mongodb.entity;

public class Count {

    private Integer count;

    public Count(){
        //For Spring Data
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    @Override
    public String toString() {
        return "Count{" +
                "count=" + count +
                '}';
    }
}