Introdução ao Morphia - Java ODM for MongoDB
1. Visão geral
Neste tutorial, vamos entender como usarMorphia, um Object Document Mapper (ODM) para MongoDB em Java.
No processo, também entenderemos o que é um ODM e como ele facilita o trabalho com o MongoDB.
2. O que é um ODM?
Para os não iniciados nesta área,MongoDB is a document-oriented database built to be distributed by nature. Bases de dados orientadas a documentos, em termos simples, gerenciam documentos, que nada mais são do quea schema-less way of organizing semi-structured data. Eles se enquadram em um guarda-chuva mais amplo e vagamente definido dos bancos de dados NoSQL, nomeados após sua aparente saída da organização tradicional dos bancos de dados SQL.
O MongoDB fornecedrivers for almost all popular programming languages like Java. Esses drivers oferecem uma camada de abstração para trabalhar com o MongoDB, de modo que não estamos trabalhando diretamente com o protocolo Wire Pense nisso como o Oracle, fornecendo uma implementação do driver JDBC para seu banco de dados relacional.
No entanto, se lembrarmos de nossos dias trabalhando diretamente com o JDBC, podemos apreciar o quão confuso ele pode ficar - especialmente em um paradigma orientado a objetos. Felizmente, temos estruturas de Mapeamento Relacional a Objetos (ORM) como o Hibernate para nosso resgate. Não é muito diferente para o MongoDB.
Embora possamos certamente trabalhar com o driver de baixo nível, ele exige muito mais clichê para realizar a tarefa. Aqui, temosa similar concept to ORM called Object Document Mapper (ODM). Morphia preenche exatamente esse espaço para a linguagem de programação Java e trabalha sobre o driver Java do MongoDB.
3. Configurando dependências
Já vimos teoria suficiente para nos colocar em algum código. Para nossos exemplos, modelaremos uma biblioteca de livros e veremos como podemos gerenciá-la no MongoDB usando Morphia.
Mas antes de começar, precisaremos configurar algumas das dependências.
3.1. MongoDB
Precisamos ter uma instância em execução do MongoDB para trabalhar. Existem várias maneiras de fazer isso, e a mais simples édownload and install community edition em nossa máquina local.
Devemos deixar todas as configurações padrão como estão, incluindo a porta na qual o MongoDB é executado.
3.2. Morfina
Podemos baixar os JARs pré-construídos para Morphia deMaven Centrale usá-los em nosso projeto Java.
No entanto, a maneira mais simples é usar uma ferramenta de gerenciamento de dependências como o Maven:
dev.morphia.morphia
core
1.5.3
4. Como conectar usando Morphia?
Agora que temos o MongoDB instalado e em execução e configuramos o Morphia em nosso projeto Java, estamos prontos para nos conectar ao MongoDB usando o Morphia.
Vamos ver como podemos fazer isso:
Morphia morphia = new Morphia();
morphia.mapPackage("com.example.morphia");
Datastore datastore = morphia.createDatastore(new MongoClient(), "library");
datastore.ensureIndexes();
É basicamente isso! Vamos entender isso melhor. Precisamos de duas coisas para que nossas operações de mapeamento funcionem:
-
Um mapeador: é responsável pormapping our Java POJOs to MongoDB Collections. Em nosso trecho de código acima,Morphia é a classe responsável por isso. Observe como estamos configurando o pacote onde ele deve procurar nossos POJOs.
-
Uma conexão: Esta é a conexão com um banco de dados MongoDB no qual o mapeador pode executar operações diferentes. A classeDatastore leva como parâmetro uma instância deMongoClient (do driver Java MongoDB) e o nome do banco de dados MongoDB,returning an active connection to work with.
Então, estamos prontos para usar esteDatastoree trabalhar com nossas entidades.
5. Como trabalhar com entidades?
Antes de podermos usar nossosDatastore recém-criados, precisamos definir algumas entidades de domínio com as quais trabalhar.
5.1. Entidade Simples
Vamos começar definindo uma entidadeBook simples com alguns 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
}
Há algumas coisas interessantes a serem observadas aqui:
-
Observe a anotação@Entity that qualifies this POJO for ODM mapping por Morphia
-
Morphia, por padrão, mapeia uma entidade para uma coleção no MongoDB pelo nome de sua classe, mas podemos substituir isso explicitamente (como fizemos para a entidadeBook aqui)
-
Morphia, por padrão, mapeia as variáveis em uma entidade para as chaves em uma coleção MongoDB pelo nome da variável, mas, novamente, podemos sobrescrever isso (como fizemos para a variávelcost aqui)
-
Por último, precisamosmark a variable in the entity to act as the primary key by the annotation @Id (como se estivéssemos usando ISBN para nosso livro aqui)
5.2. Entidades com Relacionamentos
No mundo real, porém, as entidades não são tão simples quanto parecem e têm relacionamentos complexos entre si. Por exemplo, nossa entidade simplesBook pode ter umPublishere pode fazer referência a outros livros complementares. Como os modelamos?
O MongoDB oferecetwo mechanisms to build relationships — Referencing and Embedding. Como o nome sugere, com referência, o MongoDB armazena dados relacionados como um documento separado na mesma coleção ou em uma coleção diferente e apenas faz referência a eles usando seu ID.
Pelo contrário, com a incorporação, o MongoDB armazena ou melhor, incorpora a relação no próprio documento pai.
Vamos ver como podemos usá-los. Vamos começar incorporandoPublisher em nossoBook:
@Embedded
private Publisher publisher;
Simples o suficiente. Agora vamos adicionar referências a outros livros:
@Reference
private List companionBooks;
É isso - o Morphia fornece anotações convenientes para modelar relacionamentos conforme suportado pelo MongoDB. A escolha dereferencing vs embedding, however, should draw from data model complexity, redundancy, and consistency entre outras considerações.
O exercício é semelhante à normalização em bancos de dados relacionais.
Agora, estamos prontos para realizar algumas operações emBook usandoDatastore.
6. Algumas operações básicas
Vamos ver como trabalhar com algumas das operações básicas usando Morphia.
6.1. Save
Vamos começar com a mais simples das operações, criando uma instância deBook em nosso banco de dados MongoDBlibrary:
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);
Isso é suficiente para permitir que o Morphia crie uma coleção em nosso banco de dados MongoDB, se não existir, e execute uma operação de upsert.
6.2. Inquerir
Vamos ver se conseguimos consultar o livro que acabamos de criar no MongoDB:
List books = datastore.createQuery(Book.class)
.field("title")
.contains("Learning Java")
.find()
.toList();
assertEquals(1, books.size());
assertEquals(book, books.get(0));
A consulta de um documento no Morphia começa com a criação de uma consulta usandoDatastoree, em seguida, adicionando filtros de forma declarativa, para o deleite dos apaixonados por programação funcional!
Morphia suporta muito maiscomplex query construction with filters and operators. Além disso, o Morphia permite limitar, pular e ordenar os resultados na consulta.
Além do mais, o Morphia nos permite usar consultas brutas escritas com o driver Java para MongoDB para obter mais controle, caso seja necessário.
6.3. Atualizar
Embora uma operação de salvamento possa manipular atualizações se a chave primária corresponder, o Morphia fornece maneiras de atualizar seletivamente os documentos:
Query query = datastore.createQuery(Book.class)
.field("title")
.contains("Learning Java");
UpdateOperations updates = datastore.createUpdateOperations(Book.class)
.inc("price", 1);
datastore.update(query, updates);
List books = datastore.createQuery(Book.class)
.field("title")
.contains("Learning Java")
.find()
.toList();
assertEquals(4.95, books.get(0).getCost());
Aqui, estamos construindo uma consulta e uma operação de atualização para aumentar em um o preço de todos os livros retornados pela consulta.
6.4. Excluir
Finalmente, o que foi criado deve ser excluído! Novamente, com Morphia, é bastante intuitivo:
Query query = datastore.createQuery(Book.class)
.field("title")
.contains("Learning Java");
datastore.delete(query);
List books = datastore.createQuery(Book.class)
.field("title")
.contains("Learning Java")
.find()
.toList();
assertEquals(0, books.size());
Criamos a consulta de forma bastante semelhante a antes e executamos a operação de exclusão emDatastore.
7. Uso Avançado
MongoDB temsome advanced operations like Aggregation, Indexing, and many others. Embora não seja possível realizar tudo isso usando Morphia, certamente é possível conseguir um pouco disso. Para outros, infelizmente, teremos que recorrer ao driver Java para MongoDB.
Vamos nos concentrar em algumas dessas operações avançadas que podemos realizar por meio do Morphia.
7.1. Agregação
A agregação no MongoDB nos permite definira series of operations in a pipeline that can operate on a set of documents and produce aggregated output.
A Morphia possui uma API para suportar esse pipeline de agregação.
Vamos supor que desejamos agregar os dados de nossa biblioteca de forma que todos os livros sejam agrupados por seu autor:
Iterator iterator = datastore.createAggregation(Book.class)
.group("author", grouping("books", push("title")))
.out(Author.class);
Então, como isso funciona? Começamos criando um pipeline de agregação usando os mesmosDatastore antigos. Temos que fornecer a entidade na qual desejamos realizar operações de agregação, por exemplo,Book aqui.
Em seguida, queremos agrupar documentos por "autor" e agregar seu "título" em uma chave chamada "livros". Finalmente, estamos trabalhando com um ODM aqui. Portanto, temos que definir uma entidade para coletar nossos dados agregados - no nosso caso, éAuthor.
Claro, temos que definir uma entidade chamadaAuthor com uma variável chamada books:
@Entity
public class Author {
@Id
private String name;
private List books;
// other necessary getters and setters
}
Isso, é claro, apenas arranha a superfície de uma construção muito poderosa fornecida pelo MongoDB ecan be explored further for details.
7.2. Projeção
A projeção no MongoDB nos permiteselect only the fields we want to fetch from documents in our queries. Caso a estrutura do documento seja complexa e pesada, isso pode ser realmente útil quando precisarmos de apenas alguns campos.
Suponhamos que só precisemos buscar livros com seus títulos em nossa consulta:
List 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());
Aqui, como podemos ver, apenas recuperamos o título em nosso resultado e não o autor e outros campos. No entanto, devemos ter cuidado ao usar a saída projetada para salvar novamente no MongoDB. Isso pode resultar em perda de dados!
7.3. Indexação
Os índices desempenham um papel muito importante na otimização de consultas com bancos de dados - tanto relacionais quanto não-relacionais.
MongoDBdefines indexes at the level of the collection with a unique index created on the primary key by default. Além disso, o MongoDB permite a criação de índices em qualquer campo ou subcampo de um documento. Deveríamos optar por criar um índice em uma chave, dependendo da consulta que desejamos criar.
Por exemplo, em nosso exemplo, podemos desejar criar um índice no campo “título” deBook, pois muitas vezes acabamos fazendo uma consulta nele:
@Indexes({
@Index(
fields = @Field("title"),
options = @IndexOptions(name = "book_title")
)
})
public class Book {
// ...
@Property
private String title;
// ...
}
Obviamente, podemos passar opções adicionais de indexação para personalizar as nuances do índice que é criado. Observe que o campo deve ser anotado por @Property para ser usado em um índice.
Além disso, além do índice em nível de classe, a Morphia também possui uma anotação para definir um índice em nível de campo.
7.4. Validação de esquema
Temos a opção de fornecerdata validation rules for a collection that MongoDB can use while performing an update or insert operation. Morphia suporta isso através de suas APIs.
Digamos que não queremos inserir um livro sem um preço válido. Podemos aproveitar a validação de esquema para conseguir isso:
@Validation("{ price : { $gt : 0 } }")
public class Book {
// ...
@Property("price")
private double cost;
// ...
}
Háa rich set of validations provided by MongoDB que pode ser empregado aqui.
8. ODMs MongoDB alternativos
O Morphia não é o único MongoDB ODM para Java disponível. Existem vários outros que podemos considerar usar em nossos aplicativos. Uma discussão sobre comparação com Morphia não é possível aqui, mas é sempre útil conhecer nossas opções:
-
Spring Data: fornece um modelo de programação baseado em Spring para trabalhar com MongoDB
-
MongoJack: fornece mapeamento direto de objetos JSON para MongoDB
Esta não é uma lista completa dos ODMs do MongoDB para Java, mas existem algumas alternativas interessantes disponíveis!
9. Conclusão
Neste artigo, entendemos os detalhes básicos do MongoDB e o uso de um ODM para conectar e operar no MongoDB a partir de uma linguagem de programação como Java. Exploramos ainda o Morphia como um ODM do MongoDB para Java e os vários recursos que ele possui.
Como sempre, o código pode ser encontradoover on GitHub.