sql >> Base de Datos >  >> NoSQL >> MongoDB

Introducción a Spring Data MongoDB

1. Resumen

Este artículo será una introducción rápida y práctica a Spring Data MongoDB.

Repasaremos los conceptos básicos utilizando tanto la MongoTemplate así como MongoRepository , con ejemplos prácticos para ilustrar cada operación.


Lectura adicional:

Soporte geoespacial en MongoDB

Eche un vistazo a cómo almacenar, indexar y buscar datos geoespaciales con MongoDBLeer más →

Pruebas de integración de Spring Boot con MongoDB incorporado

Aprenda a usar la solución MongoDB integrada de Flapdoodle junto con Spring Boot para ejecutar las pruebas de integración de MongoDB sin problemas. Leer más →

2. Plantilla Mongo y MongoRepository

La Plantilla Mongo sigue el patrón de plantilla estándar en Spring y proporciona una API básica lista para usar para el motor de persistencia subyacente.

El repositorio sigue el enfoque centrado en Spring Data y viene con operaciones de API más flexibles y complejas, basadas en los patrones de acceso bien conocidos en todos los proyectos de Spring Data.

Para ambos, debemos comenzar definiendo la dependencia, por ejemplo, en el pom.xml , con Maven:

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-mongodb</artifactId>
    <version>3.0.3.RELEASE</version>
</dependency>

Para verificar si se ha lanzado alguna nueva versión de la biblioteca, haga un seguimiento de los lanzamientos aquí.

3. Configuración para MongoTemplate

3.1. Configuración XML

Comencemos con la configuración XML simple para la plantilla de Mongo:

<mongo:mongo-client id="mongoClient" host="localhost" />
<mongo:db-factory id="mongoDbFactory" dbname="test" mongo-client-ref="mongoClient" />

Primero necesitamos definir el bean de fábrica responsable de crear instancias de Mongo.

A continuación, debemos definir (y configurar) el bean de plantilla:

<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> 
    <constructor-arg ref="mongoDbFactory"/> 
</bean>

Y finalmente, necesitamos definir un posprocesador para traducir cualquier MongoExceptions lanzado en @Repository clases anotadas:

<bean class=
  "org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

3.2. Configuración Java

Ahora creemos una configuración similar usando la configuración de Java extendiendo la clase base para la configuración de MongoDB AbstractMongoConfiguration :

@Configuration
public class MongoConfig extends AbstractMongoClientConfiguration {
 
    @Override
    protected String getDatabaseName() {
        return "test";
    }
 
    @Override
    public MongoClient mongoClient() {
        ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test");
        MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
            .applyConnectionString(connectionString)
            .build();
        
        return MongoClients.create(mongoClientSettings);
    }
 
    @Override
    public Collection getMappingBasePackages() {
        return Collections.singleton("com.baeldung");
    }
}

Tenga en cuenta que no necesitamos definir MongoTemplate bean en la configuración anterior ya que ya está definido en AbstractMongoClientConfiguration .

También podemos usar nuestra configuración desde cero sin extender AbstractMongoClientConfiguration :

@Configuration
public class SimpleMongoConfig {
 
    @Bean
    public MongoClient mongo() {
        ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test");
        MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
          .applyConnectionString(connectionString)
          .build();
        
        return MongoClients.create(mongoClientSettings);
    }

    @Bean
    public MongoTemplate mongoTemplate() throws Exception {
        return new MongoTemplate(mongo(), "test");
    }
}

4. Configuración para MongoRepository

4.1. Configuración XML

Para hacer uso de repositorios personalizados (ampliando el MongoRepository ), necesitamos continuar con la configuración desde la sección 3.1. y configurar los repositorios:

<mongo:repositories 
  base-package="com.baeldung.repository" mongo-template-ref="mongoTemplate"/>

4.2. Configuración Java

Del mismo modo, nos basaremos en la configuración que ya creamos en la sección 3.2. y agregue una nueva anotación a la mezcla:

@EnableMongoRepositories(basePackages = "com.baeldung.repository")

4.3. Crear el Repositorio

Después de la configuración, necesitamos crear un repositorio, extendiendo el MongoRepository existente interfaz:

public interface UserRepository extends MongoRepository<User, String> {
    // 
}

Ahora podemos conectar automáticamente este UserRepository y usar operaciones de MongoRepository o agregar operaciones personalizadas.

5. Uso de MongoTemplate

5.1. Insertar

Comencemos con la operación de inserción y una base de datos vacía:

{
}

Ahora si insertamos un nuevo usuario:

User user = new User();
user.setName("Jon");
mongoTemplate.insert(user, "user");

la base de datos se verá así:

{
    "_id" : ObjectId("55b4fda5830b550a8c2ca25a"),
    "_class" : "com.baeldung.model.User",
    "name" : "Jon"
}

5.2. Guardar – Insertar

El guardar La operación tiene una semántica de guardar o actualizar:si hay una identificación, realiza una actualización y, si no, realiza una inserción.

Veamos la primera semántica:la inserción.

Este es el estado inicial de la base de datos:

{
}

Cuando ahora guardamos un nuevo usuario:

User user = new User();
user.setName("Albert"); 
mongoTemplate.save(user, "user");

la entidad se insertará en la base de datos:

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "com.baeldung.model.User",
    "name" : "Albert"
}

A continuación, veremos la misma operación:guardar — con actualización de semántica.

5.3. Guardar – Actualizar

Veamos ahora guardar con semántica de actualización, operando en una entidad existente:

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "com.baeldung.model.User",
    "name" : "Jack"
}

Cuando ahorramos el usuario existente, lo actualizaremos:

user = mongoTemplate.findOne(
  Query.query(Criteria.where("name").is("Jack")), User.class);
user.setName("Jim");
mongoTemplate.save(user, "user");

La base de datos se verá así:

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "com.baeldung.model.User",
    "name" : "Jim"
}

Podemos ver que en este ejemplo particular, guardar usa la semántica de update porque usamos un objeto con _id dado .

5.4. Actualizar primero

actualizar primero actualiza el primer documento que coincide con la consulta.

Comencemos con el estado inicial de la base de datos:

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Alex"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
        "_class" : "com.baeldung.model.User",
        "name" : "Alex"
    }
]

Cuando ahora ejecutamos updateFirst :

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Alex"));
Update update = new Update();
update.set("name", "James");
mongoTemplate.updateFirst(query, update, User.class);

solo se actualizará la primera entrada:

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "James"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
        "_class" : "com.baeldung.model.User",
        "name" : "Alex"
    }
]

5.5. Actualizar Múltiples

Actualizar Múltiples actualiza todos los documentos que coinciden con la consulta dada.

Primero, este es el estado de la base de datos antes de hacer updateMulti :

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Eugen"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
        "_class" : "com.baeldung.model.User",
        "name" : "Eugen"
    }
]

Ahora ejecutemos updateMulti operación:

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Eugen"));
Update update = new Update();
update.set("name", "Victor");
mongoTemplate.updateMulti(query, update, User.class);

Ambos objetos existentes se actualizarán en la base de datos:

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Victor"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
        "_class" : "com.baeldung.model.User",
        "name" : "Victor"
    }
]

5.6. Buscar y modificar

Esta operación funciona como updateMulti , pero devuelve el objeto antes de que se modificara.

Primero, este es el estado de la base de datos antes de llamar a findAndModify :

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Markus"
}

Veamos el código de operación real:

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Markus"));
Update update = new Update();
update.set("name", "Nick");
User user = mongoTemplate.findAndModify(query, update, User.class);

El objeto de usuario devuelto tiene los mismos valores que el estado inicial en la base de datos.

Sin embargo, este es el nuevo estado en la base de datos:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Nick"
}

5.7. Insertar

El trastorno funciona en buscar y modificar otra cosa crear semántica :si el documento coincide, actualícelo o cree un nuevo documento combinando la consulta y el objeto de actualización.

Comencemos con el estado inicial de la base de datos:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Markus"
}

Ahora vamos a ejecutar upsert :

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Markus"));
Update update = new Update();
update.set("name", "Nick");
mongoTemplate.upsert(query, update, User.class);

Este es el estado de la base de datos después de la operación:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Nick"
}

5.8. Eliminar

Veremos el estado de la base de datos antes de llamar a remove :

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Benn"
}

Ahora ejecutemos remove :

mongoTemplate.remove(user, "user");

El resultado será el esperado:

{
}

6. Uso de MongoRepository

6.1. Insertar

Primero, veremos el estado de la base de datos antes de ejecutar insertar :

{
}

Ahora insertaremos un nuevo usuario:

User user = new User();
user.setName("Jon");
userRepository.insert(user);

Y aquí está el estado final de la base de datos:

{
    "_id" : ObjectId("55b4fda5830b550a8c2ca25a"),
    "_class" : "com.baeldung.model.User",
    "name" : "Jon"
}

Tenga en cuenta que la operación funciona igual que insertar en la Plantilla Mongo API.

6.2. Guardar Insertar

Del mismo modo, guardar funciona igual que guardar operación en MongoTemplate API.

Comencemos mirando la semántica de inserción de la operación.

Este es el estado inicial de la base de datos:

{
}

Ahora ejecutamos el save operación:

User user = new User();
user.setName("Aaron");
userRepository.save(user);

Esto da como resultado que el usuario se agregue a la base de datos:

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "com.baeldung.model.User",
    "name" : "Aaron"
}

Observe de nuevo cómo guardar funciona con insertar semántica porque estamos insertando un nuevo objeto.

6.3. Guardar Actualizar

Veamos ahora la misma operación pero con actualizar semántica.

Primero, este es el estado de la base de datos antes de ejecutar el nuevo guardar :

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "com.baeldung.model.User",
    "name" : "Jack"81*6
}

Ahora ejecutamos la operación:

user = mongoTemplate.findOne(
  Query.query(Criteria.where("name").is("Jack")), User.class);
user.setName("Jim");
userRepository.save(user);

Finalmente, aquí está el estado de la base de datos:

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "com.baeldung.model.User",
    "name" : "Jim"
}

Observe de nuevo cómo guardar funciona con actualizar semántica porque estamos usando un objeto existente.

6.4. Eliminar

Este es el estado de la base de datos antes de llamar a delete :

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Benn"
}

Ejecutemos eliminar :

userRepository.delete(user);

Y aquí está nuestro resultado:

{
}

6.5. Encuentre uno

A continuación, este es el estado de la base de datos cuando findOne se llama:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Chris"
}

Ahora ejecutemos findOne :

userRepository.findOne(user.getId())

Y el resultado devolverá los datos existentes:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Chris"
}

6.6. Existe

El estado de la base de datos antes de llamar existe :

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Harris"
}

Ahora vamos a ejecutar existe , que por supuesto devolverá verdadero :

boolean isExists = userRepository.exists(user.getId());

6.7. Buscar todo Con Ordenar

El estado de la base de datos antes de llamar a findAll :

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Brendan"
    },
    {
        "_id" : ObjectId("67b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Adam"
    }
]

Ahora ejecutemos findAll con Ordenar :

List<User> users = userRepository.findAll(Sort.by(Sort.Direction.ASC, "name"));

El resultado será ordenado por nombre en orden ascendente :

[
    {
        "_id" : ObjectId("67b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Adam"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Brendan"
    }
]

6.8. Buscar todo Con Paginable

El estado de la base de datos antes de llamar a findAll :

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Brendan"
    },
    {
        "_id" : ObjectId("67b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Adam"
    }
]

Ahora ejecutemos findAll con una solicitud de paginación:

Pageable pageableRequest = PageRequest.of(0, 1);
Page<User> page = userRepository.findAll(pageableRequest);
List<User> users = pages.getContent();

Los usuarios resultantes la lista será de un solo usuario:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Brendan"
}

7. Anotaciones

Finalmente, repasemos también las anotaciones simples que Spring Data usa para impulsar estas operaciones API.

El nivel de campo @Id la anotación puede decorar cualquier tipo, incluso larga y cadena :

@Id
private String id;

Si el valor de @Id el campo no es nulo, se almacena en la base de datos tal cual; de lo contrario, el convertidor asumirá que queremos almacenar un ObjectId en la base de datos (ya sea ObjectId , Cadena o Entero grande trabajo).

A continuación, veremos @Document :

@Document
public class User {
    //
}

Esta anotación simplemente marca una clase como un objeto de dominio que debe persistir en la base de datos, además de permitirnos elegir el nombre de la colección que se utilizará.