1. Oversigt
I denne øvelse vil vi forstå, hvordan man bruger Morphia, en Object Document Mapper (ODM) til MongoDB i Java.
I processen vil vi også forstå, hvad en ODM er, og hvordan det letter arbejdet med MongoDB.
2. Hvad er en ODM ?
For de uindviede på dette område er MongoDB en dokumentorienteret database bygget til at blive distribueret af naturen . Dokumentorienterede databaser administrerer i enkle vendinger dokumenter, som ikke er andet end en skemafri måde at organisere semistrukturerede data på . De falder ind under en bredere og løst defineret paraply af NoSQL-databaser, opkaldt efter deres tilsyneladende afvigelse fra den traditionelle organisation af SQL-databaser.
MongoDB leverer drivere til næsten alle populære programmeringssprog som Java . Disse drivere tilbyder et lag af abstraktion til at arbejde med MongoDB, så vi ikke arbejder med Wire Protocol direkte. Tænk på dette som at Oracle leverer en implementering af JDBC-driveren til deres relationelle database.
Men hvis vi husker vores dage med at arbejde med JDBC direkte, kan vi forstå, hvor rodet det kan blive - især i et objektorienteret paradigme. Heldigvis har vi Object Relational Mapping (ORM) rammer som Hibernate til vores redning. Det er ikke meget anderledes for MongoDB.
Selvom vi bestemt kan arbejde med lavniveaudriveren, kræver det meget mere kedelplade at udføre opgaven. Her har vi et lignende koncept som ORM kaldet Object Document Mapper (ODM) . Morphia fylder præcis den plads til Java-programmeringssproget og fungerer oven på Java-driveren til MongoDB.
3. Opsætning af afhængigheder
Vi har set nok teori til at få os ind i noget kode. For vores eksempler vil vi modellere et bibliotek af bøger og se, hvordan vi kan administrere det i MongoDB ved hjælp af Morphia.
Men før vi begynder, bliver vi nødt til at konfigurere nogle af afhængighederne.
3.1. MongoDB
Vi skal have en kørende forekomst af MongoDB at arbejde med. Der er flere måder at få dette på, og den enkleste er at downloade og installere community-udgaven på vores lokale maskine.
Vi bør lade alle standardkonfigurationer være som de er, inklusive den port, som MongoDB kører på.
3.2. Morphia
Vi kan downloade de forudbyggede JAR'er til Morphia fra Maven Central og bruge dem i vores Java-projekt.
Den enkleste måde er dog at bruge et afhængighedsstyringsværktøj som Maven:
<dependency>
<groupId>dev.morphia.morphia</groupId>
<artifactId>core</artifactId>
<version>1.5.3</version>
</dependency>
4. Hvordan forbinder man ved hjælp af Morphia?
Nu hvor vi har MongoDB installeret og kørende og har sat Morphia op i vores Java-projekt, er vi klar til at oprette forbindelse til MongoDB ved hjælp af Morphia.
Lad os se, hvordan vi kan opnå det:
Morphia morphia = new Morphia();
morphia.mapPackage("com.baeldung.morphia");
Datastore datastore = morphia.createDatastore(new MongoClient(), "library");
datastore.ensureIndexes();
Det er stort set det! Lad os forstå dette bedre. Vi har brug for to ting, for at vores kortlægningsoperationer kan fungere:
- En kortlægger:Denne er ansvarlig for at kortlægge vores Java POJO'er til MongoDB-samlinger . I vores kodestykke ovenfor, Morphia er klassen ansvarlig for det. Bemærk, hvordan vi konfigurerer pakken, hvor den skal søge efter vores POJO'er.
- En forbindelse:Dette er forbindelsen til en MongoDB-database, som mapperen kan udføre forskellige operationer på. Klassen Datastore tager som parameter en forekomst af MongoClient (fra Java MongoDB-driveren) og navnet på MongoDB-databasen, returnerer en aktiv forbindelse til at arbejde med .
Så vi er klar til at bruge denne Datastore og arbejde med vores enheder.
5. Hvordan arbejder man med enheder?
Før vi kan bruge vores nyslåede Datastore , er vi nødt til at definere nogle domæneentiteter at arbejde med.
5.1. Simpel enhed
Lad os starte med at definere en simpel bog enhed med nogle attributter:
@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
}
Der er et par interessante ting at bemærke her:
- Bemærk annotationen @Enhed der kvalificerer denne POJO til ODM-kortlægning af Morphia
- Morphia kortlægger som standard en enhed til en samling i MongoDB efter navnet på dens klasse, men vi kan eksplicit tilsidesætte dette (som vi har gjort for entiteten Book her)
- Morphia kortlægger som standard variablerne i en enhed til nøglerne i en MongoDB-samling med navnet på variablen, men igen kan vi tilsidesætte dette (som vi har gjort for variablen omkostning her)
- Til sidst skal vi markere en variabel i entiteten for at fungere som den primære nøgle med annotationen @Id (som vi bruger ISBN til vores bog her)
5.2. Enheder med relationer
I den virkelige verden er entiteter dog knap så enkle, som de ser ud og har komplekse forhold til hinanden. For eksempel vores simple enhed Book kan have en udgiver og kan henvise til andre ledsagende bøger. Hvordan modellerer vi dem?
MongoDB tilbyder to mekanismer til at opbygge relationer — Reference og Embedding . Som navnet antyder, med henvisning, gemmer MongoDB relaterede data som et separat dokument i den samme eller en anden samling og refererer blot til det ved hjælp af dets id.
Tværtimod, med indlejring gemmer MongoDB eller rettere indlejrer relationen i selve det overordnede dokument.
Lad os se, hvordan vi kan bruge dem. Lad os begynde med at integrere Udgiver i vores bog :
@Embedded
private Publisher publisher;
Simpelt nok. Lad os nu gå videre og tilføje referencer til andre bøger:
@Reference
private List<Book> companionBooks;
Det var det – Morphia giver praktiske annotationer til modellering af relationer som understøttet af MongoDB. Valget af henvisning vs indlejring bør dog trække fra datamodelkompleksitet, redundans og konsistens blandt andre overvejelser.
Øvelsen ligner normalisering i relationelle databaser.
Nu er vi klar til at udføre nogle handlinger på Book ved hjælp af Datastore .
6. Nogle grundlæggende handlinger
Lad os se, hvordan man arbejder med nogle af de grundlæggende handlinger ved hjælp af Morphia.
6.1. Gem
Lad os begynde med den enkleste af handlingerne ved at oprette en forekomst af Book i vores MongoDB database 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);
Dette er nok til at lade Morphia oprette en samling i vores MongoDB-database, hvis den ikke eksisterer, og udføre en upsert-operation.
6.2. Forespørgsel
Lad os se, om vi er i stand til at forespørge på den bog, vi lige har oprettet 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));
Forespørgsel efter et dokument i Morphia begynder med at oprette en forespørgsel ved hjælp af Datastore og derefter deklarativt tilføje filtre til glæde for dem, der er forelsket i funktionel programmering!
Morphia understøtter meget mere kompleks forespørgselskonstruktion med filtre og operatører. Desuden giver Morphia mulighed for at begrænse, springe over og bestille resultater i forespørgslen.
Hvad mere er, giver Morphia os mulighed for at bruge rå forespørgsler skrevet med Java-driveren til MongoDB for mere kontrol, hvis det skulle være nødvendigt.
6.3. Opdater
Selvom en lagringshandling kan håndtere opdateringer, hvis den primære nøgle matcher, tilbyder Morphia måder at selektivt opdatere dokumenter på:
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());
Her bygger vi en forespørgsel og en opdateringsoperation for at øge prisen med én af alle bøger, der returneres af forespørgslen.
6.4. Slet
Endelig skal det oprettede slettes! Igen, med Morphia er det ret 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 opretter forespørgslen helt på samme måde som før og kører sletningsoperationen på Datastore .
7. Avanceret brug
MongoDB har nogle avancerede operationer som aggregation, indeksering og mange andre . Selvom det ikke er muligt at udføre alt det ved hjælp af Morphia, er det bestemt muligt at opnå noget af det. For andre bliver vi desværre nødt til at falde tilbage til Java-driveren til MongoDB.
Lad os fokusere på nogle af disse avancerede operationer, som vi kan udføre gennem Morphia.
7.1. Aggregation
Aggregering i MongoDB giver os mulighed for at definere en række operationer i en pipeline, der kan operere på et sæt dokumenter og producere aggregeret output .
Morphia har et API til at understøtte en sådan aggregeringspipeline.
Lad os antage, at vi ønsker at aggregere vores biblioteksdata på en sådan måde, at vi har alle bøgerne grupperet efter deres forfatter:
Iterator<Author> iterator = datastore.createAggregation(Book.class)
.group("author", grouping("books", push("title")))
.out(Author.class);
Så hvordan virker dette? Vi begynder med at oprette en aggregeringspipeline ved hjælp af det samme gamle Datastore . Vi skal levere den enhed, som vi ønsker at udføre aggregeringsoperationer på, for eksempel Book her.
Dernæst vil vi gruppere dokumenter efter "forfatter" og samle deres "titel" under en nøgle kaldet "bøger". Endelig arbejder vi med en ODM her. Så vi er nødt til at definere en enhed til at indsamle vores aggregerede data - i vores tilfælde er det Forfatter .
Selvfølgelig skal vi definere en enhed kaldet Author med en variabel kaldet bøger:
@Entity
public class Author {
@Id
private String name;
private List<String> books;
// other necessary getters and setters
}
Dette ridser selvfølgelig bare overfladen af en meget kraftfuld konstruktion leveret af MongoDB og kan udforskes yderligere for detaljer.
7.2. Projektion
Projektion i MongoDB giver os mulighed for kun at vælge de felter, vi ønsker at hente fra dokumenter i vores forespørgsler . Hvis dokumentstrukturen er kompleks og tung, kan dette være virkelig nyttigt, når vi kun har brug for nogle få felter.
Lad os antage, at vi kun behøver at hente bøger med deres titel i vores forespørgsel:
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());
Her får vi, som vi kan se, kun titlen tilbage i vores resultat og ikke forfatteren og andre felter. Vi bør dog være forsigtige med at bruge det forventede output til at gemme tilbage til MongoDB. Dette kan resultere i tab af data!
7.3. Indeksering
Indekser spiller en meget vigtig rolle i forespørgselsoptimering med databaser - relationelle såvel som mange ikke-relationelle.
MongoDB definerer indekser på samlingsniveauet med et unikt indeks oprettet på den primære nøgle som standard . Desuden tillader MongoDB at oprette indekser på ethvert felt eller underfelt i et dokument. Vi bør vælge at oprette et indeks på en nøgle afhængigt af den forespørgsel, vi ønsker at oprette.
For eksempel kan vi i vores eksempel ønske at oprette et indeks i feltet "title" på Bog da vi ofte ender med at spørge efter det:
@Indexes({
@Index(
fields = @Field("title"),
options = @IndexOptions(name = "book_title")
)
})
public class Book {
// ...
@Property
private String title;
// ...
}
Selvfølgelig kan vi videregive yderligere indekseringsmuligheder for at skræddersy nuancerne i det indeks, der bliver oprettet. Bemærk, at feltet skal være kommenteret med @Ejendom skal bruges i et indeks.
Bortset fra indekset på klasseniveau har Morphia desuden en annotation til at definere et indeks på feltniveau.
7.4. Skemavalidering
Vi har en mulighed for at give datavalideringsregler for en samling, som MongoDB kan bruge, mens han udfører en opdatering eller indsættelseshandling . Morphia understøtter dette gennem deres API'er.
Lad os sige, at vi ikke ønsker at indsætte en bog uden en gyldig pris. Vi kan udnytte skemavalidering til at opnå dette:
@Validation("{ price : { $gt : 0 } }")
public class Book {
// ...
@Property("price")
private double cost;
// ...
}
Der er et rigt sæt af valideringer leveret af MongoDB, som kan bruges her.
8. Alternative MongoDB ODM'er
Morphia er ikke den eneste tilgængelige MongoDB ODM til Java. Der er flere andre, som vi kan overveje at bruge i vores applikationer. En diskussion om sammenligning med Morphia er ikke mulig her, men det er altid nyttigt at kende vores muligheder:
- Forårsdata:Giver en Spring-baseret programmeringsmodel til at arbejde med MongoDB
- MongoJack:Giver direkte kortlægning fra JSON til MongoDB-objekter
Dette er ikke en komplet liste over MongoDB ODM'er til Java, men der er nogle interessante alternativer til rådighed!