sql >> Databasteknik >  >> NoSQL >> MongoDB

Introduktion till Morphia – Java ODM för MongoDB

1. Översikt

I den här handledningen kommer vi att förstå hur man använder Morphia, en Object Document Mapper (ODM) för MongoDB i Java.

I processen kommer vi också att förstå vad en ODM är och hur det underlättar arbetet med MongoDB.

2. Vad är en ODM ?

För de oinitierade inom detta område är MongoDB en dokumentorienterad databas byggd för att distribueras av naturen . Dokumentorienterade databaser, enkelt uttryckt, hanterar dokument, som inte är något annat än ett schemalöst sätt att organisera semistrukturerad data . De faller under ett bredare och löst definierat paraply av NoSQL-databaser, uppkallade efter deras uppenbara avvikelse från den traditionella organisationen av SQL-databaser.

MongoDB tillhandahåller drivrutiner för nästan alla populära programmeringsspråk som Java . Dessa drivrutiner erbjuder ett lager av abstraktion för att arbeta med MongoDB så att vi inte arbetar med Wire Protocol direkt. Se det här som att Oracle tillhandahåller en implementering av JDBC-drivrutinen för deras relationsdatabas.

Men om vi minns våra dagar som vi arbetade med JDBC direkt, kan vi uppskatta hur rörigt det kan bli - särskilt i ett objektorienterat paradigm. Lyckligtvis har vi Object Relational Mapping (ORM) ramverk som Hibernate till vår räddning. Det är inte särskilt annorlunda för MongoDB.

Även om vi förvisso kan arbeta med lågnivådrivrutinen, kräver det mycket mer planlösning för att utföra uppgiften. Här har vi ett liknande koncept som ORM som kallas Object Document Mapper (ODM) . Morphia fyller exakt det utrymmet för programmeringsspråket Java och fungerar ovanpå Java-drivrutinen för MongoDB.

3. Ställa in beroenden

Vi har sett tillräckligt med teori för att få in oss i någon kod. För våra exempel kommer vi att modellera ett bibliotek med böcker och se hur vi kan hantera det i MongoDB med Morphia.

Men innan vi börjar måste vi ställa in några av beroenden.

3.1. MongoDB

Vi måste ha en körande instans av MongoDB att arbeta med. Det finns flera sätt att få detta, och det enklaste är att ladda ner och installera community-utgåvan på vår lokala dator.

Vi bör lämna alla standardkonfigurationer som de är, inklusive porten som MongoDB körs på.

3.2. Morphia

Vi kan ladda ner de förbyggda JAR:erna för Morphia från Maven Central och använda dem i vårt Java-projekt.

Det enklaste sättet är dock att använda ett beroendehanteringsverktyg som Maven:

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

4. Hur ansluter jag med Morphia?

Nu när vi har MongoDB installerat och kört och har konfigurerat Morphia i vårt Java-projekt, är vi redo att ansluta till MongoDB med Morphia.

Låt oss se hur vi kan åstadkomma det:

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

Det är ganska mycket det! Låt oss förstå detta bättre. Vi behöver två saker för att vår kartläggning ska fungera:

  1. En kartläggare:Den här är ansvarig för att karta våra Java POJOs till MongoDB-samlingar . I vårt kodavsnitt ovan, Morphia är klassen ansvarig för det. Notera hur vi konfigurerar paketet där det ska leta efter våra POJOs.
  2. En anslutning:Detta är anslutningen till en MongoDB-databas på vilken mapparen kan utföra olika operationer. Klassen Datastore tar en instans av MongoClient som parameter (från Java MongoDB-drivrutinen) och namnet på MongoDB-databasen, återställer en aktiv anslutning att arbeta med .

Så vi är alla redo att använda denna Datastore och arbeta med våra enheter.

5. Hur arbetar man med enheter?

Innan vi kan använda vår nypräglade Datastore , måste vi definiera några domänenheter att arbeta med.

5.1. Enkel enhet

Låt oss börja med att definiera en enkel bok enhet med några attribut:

@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
}

Det finns ett par intressanta saker att notera här:

  • Lägg märke till anteckningen @Entitet som kvalificerar denna POJO för ODM-kartläggning av Morphia
  • Morphia mappar som standard en entitet till en samling i MongoDB med namnet på dess klass, men vi kan uttryckligen åsidosätta detta (som vi har gjort för entiteten Book här)
  • Morphia mappar som standard variablerna i en entitet till nycklarna i en MongoDB-samling med namnet på variabeln, men återigen kan vi åsidosätta detta (som vi har gjort för variabeln kostnad här)
  • Sistligen måste vi markera en variabel i entiteten för att fungera som primärnyckel med anteckningen @Id (som vi använder ISBN för vår bok här)

5.2. Entiteter med relationer

I den verkliga världen är dock enheter knappast så enkla som de ser ut och har komplexa relationer med varandra. Till exempel vår enkla enhet Boka kan ha en utgivare och kan referera till andra kompletterande böcker. Hur modellerar vi dem?

MongoDB erbjuder två mekanismer för att bygga relationer – referens och inbäddning . Som namnet antyder, med referenser, lagrar MongoDB relaterad data som ett separat dokument i samma eller en annan samling och refererar bara till det med dess id.

Tvärtom, med inbäddning lagrar MongoDB eller snarare bäddar in relationen i själva överordnade dokumentet.

Låt oss se hur vi kan använda dem. Låt oss börja med att bädda in Utgivare i vår bok :

@Embedded
private Publisher publisher;

Enkelt nog. Låt oss nu gå vidare och lägga till referenser till andra böcker:

@Reference
private List<Book> companionBooks;

Det var allt - Morphia tillhandahåller bekväma kommentarer för att modellera relationer som stöds av MongoDB. Valet av referens kontra inbäddning bör dock bygga på datamodellens komplexitet, redundans och konsekvens bland annat.

Övningen liknar normalisering i relationsdatabaser.

Nu är vi redo att utföra några operationer på Boka med Datastore .

6. Några grundläggande funktioner

Låt oss se hur man arbetar med några av de grundläggande operationerna med Morphia.

6.1. Spara

Låt oss börja med den enklaste av operationerna, skapa en instans av Boka i vår MongoDB-databas bibliotek :

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);

Detta räcker för att låta Morphia skapa en samling i vår MongoDB-databas, om den inte finns, och utföra en upsert-operation.

6.2. Fråga

Låt oss se om vi kan fråga boken vi just skapade i MongoDB:

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

assertEquals(1, books.size());

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

Att fråga efter ett dokument i Morphia börjar med att skapa en fråga med Datastore och sedan deklarativt lägga till filter, till glädje för dem som är förälskade i funktionell programmering!

Morphia stöder mycket mer komplex frågekonstruktion med filter och operatörer. Dessutom tillåter Morphia att begränsa, hoppa över och sortera resultat i frågan.

Dessutom tillåter Morphia oss att använda råfrågor skrivna med Java-drivrutinen för MongoDB för mer kontroll, om det skulle behövas.

6.3. Uppdatera

Även om en sparoperation kan hantera uppdateringar om den primära nyckeln matchar, tillhandahåller Morphia sätt att selektivt uppdatera dokument:

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());

Här bygger vi en fråga och en uppdateringsoperation för att öka med ett pris på alla böcker som returneras av frågan.

6.4. Ta bort

Äntligen måste det som skapats raderas! Återigen, med Morphia är det ganska intuitivt:

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());

Vi skapar frågan på ungefär samma sätt som tidigare och kör borttagningen i Datastore .

7. Avancerad användning

MongoDB har vissa avancerade funktioner som aggregation, indexering och många andra . Även om det inte är möjligt att utföra allt detta med Morphia, är det säkert möjligt att uppnå en del av det. För andra måste vi tyvärr falla tillbaka till Java-drivrutinen för MongoDB.

Låt oss fokusera på några av dessa avancerade operationer som vi kan utföra genom Morphia.

7.1. Aggregation

Aggregation i MongoDB låter oss definiera en serie operationer i en pipeline som kan arbeta på en uppsättning dokument och producera aggregerad utdata .

Morphia har ett API för att stödja en sådan aggregeringspipeline.

Låt oss anta att vi vill aggregera våra biblioteksdata på ett sådant sätt att vi har alla böcker grupperade efter deras författare:

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

Så, hur fungerar det här? Vi börjar med att skapa en aggregeringspipeline med samma gamla Datastore . Vi måste tillhandahålla den enhet på vilken vi vill utföra aggregeringsoperationer, till exempel Boka här.

Därefter vill vi gruppera dokument efter "författare" och samla deras "titel" under en nyckel som kallas "böcker". Äntligen arbetar vi med en ODM här. Så vi måste definiera en enhet för att samla in vår aggregerade data – i vårt fall är det Författare .

Naturligtvis måste vi definiera en enhet som heter Author med en variabel som heter böcker:

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

Detta skrapar naturligtvis bara ytan på en mycket kraftfull konstruktion från MongoDB och kan utforskas ytterligare för detaljer.

7.2. Projektion

Projektion i MongoDB låter oss välja endast de fält vi vill hämta från dokument i våra frågor . Om dokumentstrukturen är komplex och tung kan detta vara väldigt användbart när vi bara behöver ett fåtal fält.

Låt oss anta att vi bara behöver hämta böcker med deras titel i vår fråga:

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());

Här får vi, som vi kan se, bara tillbaka titeln i vårt resultat och inte författaren och andra fält. Vi bör dock vara försiktiga med att använda den projicerade utdatan för att spara tillbaka till MongoDB. Detta kan resultera i dataförlust!

7.3. Indexering

Index spelar en mycket viktig roll vid frågeoptimering med databaser – såväl relationella som många icke-relationella.

MongoDB definierar index på samlingsnivån med ett unikt index skapat på primärnyckeln som standard . Dessutom tillåter MongoDB att index skapas på alla fält eller underfält i ett dokument. Vi bör välja att skapa ett index på en nyckel beroende på frågan vi vill skapa.

Till exempel, i vårt exempel kanske vi vill skapa ett index i fältet "titel" för Bok eftersom vi ofta frågar efter det:

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

Naturligtvis kan vi skicka ytterligare indexeringsalternativ för att skräddarsy nyanserna i indexet som skapas. Observera att fältet ska kommenteras av @Egenskap ska användas i ett index.

Bortsett från klassnivåindexet har Morphia dessutom en kommentar för att definiera ett fältnivåindex.

7.4. Schemavalidering

Vi har ett alternativ att tillhandahålla datavalideringsregler för en samling som MongoDB kan använda när en uppdatering eller infogning utförs . Morphia stödjer detta genom sina API:er.

Låt oss säga att vi inte vill infoga en bok utan ett giltigt pris. Vi kan utnyttja schemavalidering för att uppnå detta:

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

Det finns en rik uppsättning valideringar som tillhandahålls av MongoDB som kan användas här.

8. Alternativa MongoDB ODM:er

Morphia är inte den enda tillgängliga MongoDB ODM för Java. Det finns flera andra som vi kan överväga att använda i våra applikationer. En diskussion om jämförelse med Morphia är inte möjlig här, men det är alltid användbart att känna till våra alternativ:

  • Spring Data:Tillhandahåller en Spring-baserad programmeringsmodell för att arbeta med MongoDB
  • MongoJack:Ger direkt mappning från JSON till MongoDB-objekt

Det här är inte en komplett lista över MongoDB ODM:er för Java, men det finns några intressanta alternativ tillgängliga!


  1. Skicka meddelanden till grupper i Django Channels 2

  2. Högpresterande MongoDB-kluster på Azure

  3. Hur kör jag Redis på Windows 32 bit?

  4. Kör flera instanser av Redis på Centos