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

Introducción a Morphia – Java ODM para MongoDB

1. Resumen

En este tutorial, entenderemos cómo usar Morphia, un mapeador de documentos de objetos (ODM) para MongoDB en Java.

En el proceso, también entenderemos qué es un ODM y cómo facilita el trabajo con MongoDB.

2. ¿Qué es un ODM? ?

Para aquellos no iniciados en esta área, MongoDB es una base de datos orientada a documentos construida para ser distribuida por naturaleza . Las bases de datos orientadas a documentos, en términos simples, administran documentos, que no son más que una forma sin esquema de organizar datos semiestructurados. . Caen bajo un paraguas más amplio y vagamente definido de bases de datos NoSQL, llamado así por su aparente desviación de la organización tradicional de las bases de datos SQL.

MongoDB proporciona controladores para casi todos los lenguajes de programación populares como Java . Estos controladores ofrecen una capa de abstracción para trabajar con MongoDB, de modo que no estemos trabajando directamente con Wire Protocol. Piense en esto como si Oracle proporcionara una implementación del controlador JDBC para su base de datos relacional.

Sin embargo, si recordamos nuestros días trabajando directamente con JDBC, podemos apreciar lo complicado que puede llegar a ser, especialmente en un paradigma orientado a objetos. Afortunadamente, contamos con marcos de mapeo relacional de objetos (ORM) como Hibernate para nuestro rescate. No es muy diferente para MongoDB.

Si bien ciertamente podemos trabajar con el controlador de bajo nivel, requiere mucho más repetitivo para realizar la tarea. Aquí, tenemos un concepto similar a ORM llamado Object Document Mapper (ODM) . Morphia llena exactamente ese espacio para el lenguaje de programación Java y funciona sobre el controlador Java para MongoDB.

3. Configuración de dependencias

Hemos visto suficiente teoría para introducirnos en algún código. Para nuestros ejemplos, modelaremos una biblioteca de libros y veremos cómo podemos administrarla en MongoDB usando Morphia.

Pero antes de comenzar, necesitaremos configurar algunas de las dependencias.

3.1. MongoDB

Necesitamos tener una instancia en ejecución de MongoDB para trabajar. Hay varias formas de obtener esto, y la más simple es descargar e instalar la edición comunitaria en nuestra máquina local.

Deberíamos dejar todas las configuraciones predeterminadas como están, incluido el puerto en el que se ejecuta MongoDB.

3.2. Morfia

Podemos descargar los archivos JAR preconstruidos para Morphia desde Maven Central y usarlos en nuestro proyecto Java.

Sin embargo, la forma más sencilla es utilizar una herramienta de gestión de dependencias como Maven:

<dependency>
    <groupId>dev.morphia.morphia</groupId>
    <artifactId>core</artifactId>
    <version>1.5.3</version>
</dependency>

4. ¿Cómo conectarse usando Morphia?

Ahora que tenemos MongoDB instalado y ejecutándose y hemos configurado Morphia en nuestro proyecto Java, estamos listos para conectarnos a MongoDB usando Morphia.

Veamos cómo podemos lograr eso:

Morphia morphia = new Morphia();
morphia.mapPackage("com.baeldung.morphia");
Datastore datastore = morphia.createDatastore(new MongoClient(), "library");
datastore.ensureIndexes();

¡Eso es practicamente todo! Entendamos mejor esto. Necesitamos dos cosas para que nuestras operaciones de mapeo funcionen:

  1. Un asignador:este es responsable de asignar nuestros POJO de Java a las colecciones de MongoDB . En nuestro fragmento de código anterior, Morphia es la clase responsable de eso. Tenga en cuenta cómo estamos configurando el paquete donde debería buscar nuestros POJO.
  2. Una conexión:esta es la conexión a una base de datos MongoDB en la que el mapeador puede ejecutar diferentes operaciones. La clase Almacén de datos toma como parámetro una instancia de MongoClient (del controlador Java MongoDB) y el nombre de la base de datos MongoDB, devolviendo una conexión activa para trabajar .

Entonces, estamos listos para usar este Almacén de datos y trabajar con nuestras entidades.

5. ¿Cómo trabajar con entidades?

Antes de que podamos usar nuestro Datastore recién acuñado , necesitamos definir algunas entidades de dominio para trabajar.

5.1. Entidad simple

Comencemos definiendo un Libro simple entidad con algunos atributos:

@Entity("Books")
public class Book {
    @Id
    private String isbn;
    private String title;
    private String author;
    @Property("price")
    private double cost;
    // constructors, getters, setters and hashCode, equals, toString implementations
}

Hay un par de cosas interesantes a tener en cuenta aquí:

  • Observe la anotación @Entidad que califica este POJO para mapeo ODM por Morphia
  • Morphia, por defecto, asigna una entidad a una colección en MongoDB por el nombre de su clase, pero podemos anular esto explícitamente (como hemos hecho para la entidad Book aquí)
  • Morphia, de forma predeterminada, asigna las variables en una entidad a las claves en una colección MongoDB por el nombre de la variable, pero nuevamente podemos anular esto (como hemos hecho para la variable costo aquí)
  • Por último, debemos marcar una variable en la entidad para que actúe como clave principal mediante la anotación @Id (como si estuviéramos usando ISBN para nuestro libro aquí)

5.2. Entidades con Relaciones

Sin embargo, en el mundo real, las entidades no son tan simples como parecen y tienen relaciones complejas entre sí. Por ejemplo, nuestra entidad simple Libro puede tener un Editor y puede hacer referencia a otros libros complementarios. ¿Cómo los modelamos?

MongoDB ofrece dos mecanismos para construir relaciones:referencia e incrustación . Como sugiere el nombre, con las referencias, MongoDB almacena datos relacionados como un documento separado en la misma colección o en una diferente y solo hace referencia a ellos usando su id.

Por el contrario, con la incrustación, MongoDB almacena o más bien incrusta la relación dentro del propio documento principal.

Veamos cómo podemos usarlos. Comencemos por incorporar Publisher en nuestro Libro :

@Embedded
private Publisher publisher;

Suficientemente simple. Ahora sigamos adelante y agreguemos referencias a otros libros:

@Reference
private List<Book> companionBooks;

Eso es todo:Morphia proporciona anotaciones convenientes para modelar relaciones con el respaldo de MongoDB. Sin embargo, la elección de referencia frente a incrustación debe basarse en la complejidad, la redundancia y la coherencia del modelo de datos entre otras consideraciones.

El ejercicio es similar a la normalización en bases de datos relacionales.

Ahora, estamos listos para realizar algunas operaciones en Book usando Almacén de datos .

6. Algunas operaciones básicas

Veamos cómo trabajar con algunas de las operaciones básicas usando Morphia.

6.1. Guardar

Comencemos con la más simple de las operaciones, creando una instancia de Book en nuestra biblioteca de base de datos MongoDB :

Publisher publisher = new Publisher(new ObjectId(), "Awsome Publisher");

Book book = new Book("9781565927186", "Learning Java", "Tom Kirkman", 3.95, publisher);
Book companionBook = new Book("9789332575103", "Java Performance Companion", 
  "Tom Kirkman", 1.95, publisher);

book.addCompanionBooks(companionBook);

datastore.save(companionBook);
datastore.save(book);

Esto es suficiente para permitir que Morphia cree una colección en nuestra base de datos MongoDB, si no existe, y realice una operación upsert.

6.2. Consulta

Veamos si podemos consultar el libro que acabamos de crear en MongoDB:

List<Book> books = datastore.createQuery(Book.class)
  .field("title")
  .contains("Learning Java")
  .find()
  .toList();

assertEquals(1, books.size());

assertEquals(book, books.get(0));

La consulta de un documento en Morphia comienza con la creación de una consulta usando Almacén de datos y luego agregando filtros declarativamente, ¡para el deleite de los amantes de la programación funcional!

Morphia admite la construcción de consultas mucho más complejas con filtros y operadores. Además, Morphia permite limitar, omitir y ordenar los resultados de la consulta.

Además, Morphia nos permite usar consultas sin procesar escritas con el controlador Java para MongoDB para tener más control, en caso de que sea necesario.

6.3. Actualizar

Aunque una operación de guardar puede gestionar actualizaciones si la clave principal coincide, Morphia proporciona formas de actualizar documentos de forma selectiva:

Query<Book> query = datastore.createQuery(Book.class)
  .field("title")
  .contains("Learning Java");

UpdateOperations<Book> updates = datastore.createUpdateOperations(Book.class)
  .inc("price", 1);

datastore.update(query, updates);

List<Book> books = datastore.createQuery(Book.class)
  .field("title")
  .contains("Learning Java")
  .find()
  .toList();

assertEquals(4.95, books.get(0).getCost());

Aquí, estamos creando una consulta y una operación de actualización para aumentar en uno el precio de todos los libros devueltos por la consulta.

6.4. Eliminar

¡Finalmente, lo que ha sido creado debe ser borrado! Nuevamente, con Morphia, es bastante intuitivo:

Query<Book> query = datastore.createQuery(Book.class)
  .field("title")
  .contains("Learning Java");

datastore.delete(query);

List<Book> books = datastore.createQuery(Book.class)
  .field("title")
  .contains("Learning Java")
  .find()
  .toList();

assertEquals(0, books.size());

Creamos la consulta de manera bastante similar a la anterior y ejecutamos la operación de eliminación en el Almacén de datos .

7. Uso avanzado

MongoDB tiene algunas operaciones avanzadas como agregación, indexación y muchas otras . Si bien no es posible realizar todo eso con Morphia, ciertamente es posible lograr algo de eso. Para otros, lamentablemente, tendremos que recurrir al controlador Java para MongoDB.

Centrémonos en algunas de estas operaciones avanzadas que podemos realizar a través de Morphia.

7.1. Agregación

La agregación en MongoDB nos permite definir una serie de operaciones en una tubería que puede operar en un conjunto de documentos y producir resultados agregados .

Morphia tiene una API para admitir dicha canalización de agregación.

Supongamos que deseamos agregar los datos de nuestra biblioteca de tal manera que tengamos todos los libros agrupados por su autor:

Iterator<Author> iterator = datastore.createAggregation(Book.class)
  .group("author", grouping("books", push("title")))
  .out(Author.class);

¿Entonces, cómo funciona esto? Comenzamos creando una canalización de agregación utilizando el mismo antiguo Almacén de datos . Tenemos que proporcionar la entidad sobre la que deseamos realizar operaciones de agregación, por ejemplo, Libro aquí.

A continuación, queremos agrupar documentos por "autor" y agregar su "título" bajo una clave llamada "libros". Finalmente, estamos trabajando con un ODM aquí. Entonces, tenemos que definir una entidad para recopilar nuestros datos agregados; en nuestro caso, es Autor .

Por supuesto, tenemos que definir una entidad llamada Autor con una variable llamada libros:

@Entity
public class Author {
    @Id
    private String name;
    private List<String> books;
    // other necessary getters and setters
}

Esto, por supuesto, solo rasca la superficie de una construcción muy poderosa proporcionada por MongoDB y se puede explorar más para obtener más detalles.

7.2. Proyección

La proyección en MongoDB nos permite seleccionar solo los campos que queremos obtener de los documentos en nuestras consultas . En caso de que la estructura del documento sea compleja y pesada, esto puede ser realmente útil cuando solo necesitamos unos pocos campos.

Supongamos que solo necesitamos buscar libros con su título en nuestra consulta:

List<Book> books = datastore.createQuery(Book.class)
  .field("title")
  .contains("Learning Java")
  .project("title", true)
  .find()
  .toList();
 
assertEquals("Learning Java", books.get(0).getTitle());
assertNull(books.get(0).getAuthor());

Aquí, como podemos ver, solo recuperamos el título en nuestro resultado y no el autor y otros campos. Sin embargo, debemos tener cuidado al usar la salida proyectada al guardar de nuevo en MongoDB. ¡Esto puede provocar la pérdida de datos!

7.3. Indexación

Los índices juegan un papel muy importante en la optimización de consultas con bases de datos, tanto relacionales como no relacionales.

MongoDB define índices a nivel de la colección con un índice único creado en la clave principal de forma predeterminada . Además, MongoDB permite crear índices en cualquier campo o subcampo dentro de un documento. Deberíamos elegir crear un índice en una clave dependiendo de la consulta que deseamos crear.

Por ejemplo, en nuestro ejemplo, es posible que deseemos crear un índice en el campo "título" de Libro ya que a menudo terminamos consultándolo:

@Indexes({
  @Index(
    fields = @Field("title"),
    options = @IndexOptions(name = "book_title")
  )
})
public class Book {
    // ...
    @Property
    private String title;
    // ...
}

Por supuesto, podemos pasar opciones de indexación adicionales para adaptar los matices del índice que se crea. Tenga en cuenta que el campo debe estar anotado por @Propiedad para ser utilizado en un índice.

Además, además del índice de nivel de clase, Morphia también tiene una anotación para definir un índice de nivel de campo.

7.4. Validación de esquema

Tenemos una opción para proporcionar reglas de validación de datos para una colección que MongoDB puede usar mientras realiza una operación de actualización o inserción . Morphia admite esto a través de sus API.

Digamos que no queremos insertar un libro sin un precio válido. Podemos aprovechar la validación del esquema para lograr esto:

@Validation("{ price : { $gt : 0 } }")
public class Book {
    // ...
    @Property("price")
    private double cost;
    // ...
}

Hay un amplio conjunto de validaciones proporcionadas por MongoDB que se pueden emplear aquí.

8. ODM alternativos de MongoDB

Morphia no es el único MongoDB ODM disponible para Java. Hay varios otros que podemos considerar para usar en nuestras aplicaciones. Aquí no es posible una discusión sobre la comparación con Morphia, pero siempre es útil conocer nuestras opciones:

  • Spring Data:proporciona un modelo de programación basado en Spring para trabajar con MongoDB
  • MongoJack:proporciona mapeo directo de JSON a objetos MongoDB

Esta no es una lista completa de MongoDB ODM para Java, ¡pero hay algunas alternativas interesantes disponibles!


No