1. Общ преглед
В този урок ще разберем как да използваме Morphia, устройство за картографиране на обекти (ODM) за MongoDB в Java.
В процеса ще разберем също какво е ODM и как улеснява работата с MongoDB.
2. Какво е ODM ?
За непосветените в тази област, MongoDB е документно-ориентирана база данни, създадена да се разпространява по природа . Документно-ориентирани бази данни, с прости думи, управляват документи, които не са нищо друго освен безсхемен начин за организиране на полуструктурирани данни . Те попадат в по-широк и слабо дефиниран чадър на NoSQL бази данни, кръстени на тяхното очевидно отклонение от традиционната организация на SQL бази данни.
MongoDB предоставя драйвери за почти всички популярни езици за програмиране като Java . Тези драйвери предлагат слой на абстракция за работа с MongoDB, така че да не работим директно с Wire Protocol. Мислете за това като за Oracle, предоставящ реализация на JDBC драйвера за тяхната релационна база данни.
Въпреки това, ако си спомним нашите дни, когато работихме директно с JDBC, можем да оценим колко объркано може да стане – особено в обектно-ориентирана парадигма. За щастие имаме рамки за релационно картографиране на обекти (ORM), като Hibernate, за да ни спасят. Не е много различно за MongoDB.
Въпреки че със сигурност можем да работим с драйвера на ниско ниво, той изисква много повече шаблони, за да изпълним задачата. Тук имаме подобна концепция на ORM, наречена Object Document Mapper (ODM) . Morphia точно запълва това пространство за езика за програмиране Java и работи върху драйвера на Java за MongoDB.
3. Настройка на зависимости
Видяхме достатъчно теория, за да ни вкара в някакъв код. За нашите примери ще моделираме библиотека от книги и ще видим как можем да я управляваме в MongoDB с помощта на Morphia.
Но преди да започнем, ще трябва да настроим някои от зависимостите.
3.1. MongoDB
Трябва да имаме работещ екземпляр на MongoDB, с който да работим. Има няколко начина да получите това, а най-простият е да изтеглите и инсталирате изданието на общността на нашата локална машина.
Трябва да оставим всички конфигурации по подразбиране такива, каквито са, включително порта, на който работи MongoDB.
3.2. Морфия
Можем да изтеглим предварително изградените JAR за Morphia от Maven Central и да ги използваме в нашия Java проект.
Най-простият начин обаче е да използвате инструмент за управление на зависимости като Maven:
<dependency>
<groupId>dev.morphia.morphia</groupId>
<artifactId>core</artifactId>
<version>1.5.3</version>
</dependency>
4. Как да се свържете с помощта на Morphia?
Сега, когато имаме инсталиран и работещ MongoDB и сме настроили Morphia в нашия Java проект, ние сме готови да се свържем с MongoDB с помощта на Morphia.
Нека да видим как можем да постигнем това:
Morphia morphia = new Morphia();
morphia.mapPackage("com.baeldung.morphia");
Datastore datastore = morphia.createDatastore(new MongoClient(), "library");
datastore.ensureIndexes();
Това е почти всичко! Нека разберем това по-добре. Трябват ни две неща, за да работят нашите операции за картографиране:
- Mapper:Това е отговорно за картографирането на нашите Java POJO към MongoDB колекции . В нашия кодов фрагмент по-горе, Morphia класът е отговорен за това. Обърнете внимание как конфигурираме пакета, където трябва да търси нашите POJO.
- Връзка:Това е връзката с база данни MongoDB, върху която картографът може да изпълнява различни операции. Класът Datastore приема като параметър екземпляр на MongoClient (от драйвера на Java MongoDB) и името на базата данни MongoDB, връщане на активна връзка за работа с .
И така, всички сме готови да използваме това Данни и да работим с нашите организации.
5. Как да работим с обекти?
Преди да можем да използваме нашето прясно изсечено Datastore , трябва да дефинираме някои обекти на домейна, с които да работим.
5.1. Прост обект
Нека започнем с дефиниране на проста Книга обект с някои атрибути:
@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
}
Тук трябва да се отбележат няколко интересни неща:
- Забележете пояснението @Обект което квалифицира това POJO за ODM картографиране от Morphia
- Morphia, по подразбиране, съпоставя обект в колекция в MongoDB по името на неговия клас, но можем изрично да заменим това (както направихме за обекта Book тук)
- Morphia, по подразбиране, съпоставя променливите в обект с ключовете в колекция MongoDB по името на променливата, но отново можем да заменим това (както направихме за променливата cost тук)
- Накрая, трябва да маркираме променлива в обекта, за да действа като първичен ключ чрез анотацията @Id (като тук използваме ISBN за нашата книга)
5.2. Субекти с връзки
В реалния свят обаче обектите едва ли са толкова прости, колкото изглеждат, и имат сложни взаимоотношения помежду си. Например нашият прост обект Книга може да има Издател и може да препраща към други придружаващи книги. Как да ги моделираме?
MongoDB предлага два механизма за изграждане на връзки — препращане и вграждане . Както подсказва името, с препращане, MongoDB съхранява свързани данни като отделен документ в същата или различна колекция и просто ги препраща, използвайки своя идентификатор.
Напротив, с вграждането MongoDB съхранява или по-скоро вгражда връзката в самия родителски документ.
Нека видим как можем да ги използваме. Нека започнем с вграждането на Publisher в нашата книга :
@Embedded
private Publisher publisher;
Достатъчно просто. Сега нека да продължим и да добавим препратки към други книги:
@Reference
private List<Book> companionBooks;
Това е всичко — Morphia предоставя удобни анотации за моделиране на връзки, поддържани от MongoDB. Изборът на рефериране срещу вграждане обаче трябва да се основава на сложността на модела на данните, излишъка и последователността наред с други съображения.
Упражнението е подобно на нормализирането в релационни бази данни.
Сега сме готови да извършим някои операции с Book като използвате Datastore .
6. Някои основни операции
Нека да видим как да работим с някои от основните операции с помощта на Morphia.
6.1. Запазване
Нека започнем с най-простата от операциите, създавайки екземпляр на Book в нашата библиотека база данни 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);
Това е достатъчно, за да позволи на Morphia да създаде колекция в нашата база данни MongoDB, ако тя не съществува, и да изпълни операция upsert.
6.2. Запитване
Нека да видим дали можем да направим заявка за книгата, която току-що създадохме в MongoDB:
List<Book> books = datastore.createQuery(Book.class)
.field("title")
.contains("Learning Java")
.find()
.toList();
assertEquals(1, books.size());
assertEquals(book, books.get(0));
Запитването на документ в Morphia започва със създаване на заявка с помощта на Datastore и след това декларативно добавяне на филтри, за радост на тези, които обичат функционалното програмиране!
Morphia поддържа много по-сложна конструкция на заявки с филтри и оператори. Освен това Morphia позволява ограничаване, пропускане и подреждане на резултатите в заявката.
Нещо повече, Morphia ни позволява да използваме необработени заявки, написани с драйвера на Java за MongoDB за повече контрол, ако това е необходимо.
6.3. Актуализиране
Въпреки че операцията за запис може да обработва актуализации, ако първичният ключ съвпада, Morphia предоставя начини за селективно актуализиране на документи:
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());
Тук изграждаме заявка и операция за актуализиране, за да увеличим с единица цената на всички книги, върнати от заявката.
6.4. Изтриване
И накрая, това, което е създадено, трябва да бъде изтрито! Отново, с Morphia е доста интуитивно:
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());
Създаваме заявката по подобен начин както преди и изпълняваме операцията за изтриване в Datastore .
7. Разширено използване
MongoDB има някои разширени операции като агрегиране, индексиране и много други . Въпреки че не е възможно да се изпълни всичко това с помощта на Morphia, със сигурност е възможно да се постигне част от това. За други, за съжаление, ще трябва да се върнем към драйвера на Java за MongoDB.
Нека се съсредоточим върху някои от тези разширени операции, които можем да извършим чрез Morphia.
7.1. Агрегация
Агрегацията в MongoDB ни позволява да дефинираме поредица от операции в конвейер, който може да работи с набор от документи и да произвежда обобщен резултат .
Morphia има API за поддръжка на такъв конвейер за агрегиране.
Да предположим, че искаме да обобщим данните от нашата библиотека по такъв начин, че да имаме всички книги, групирани по техния автор:
Iterator<Author> iterator = datastore.createAggregation(Book.class)
.group("author", grouping("books", push("title")))
.out(Author.class);
И така, как става това? Започваме със създаване на конвейер за агрегиране, използвайки същото старо Datastore . Трябва да предоставим обекта, върху който искаме да извършим операции за агрегиране, например Книга тук.
След това искаме да групираме документи по „автор“ и да обобщим тяхното „заглавие“ под ключ, наречен „книги“. И накрая, ние работим с ODM тук. Така че трябва да дефинираме обект, който да събира нашите обобщени данни — в нашия случай това е Автор .
Разбира се, трябва да дефинираме обект, наречен Автор с променлива, наречена books:
@Entity
public class Author {
@Id
private String name;
private List<String> books;
// other necessary getters and setters
}
Това, разбира се, само надрасква повърхността на много мощна конструкция, предоставена от MongoDB и може да бъде проучена допълнително за подробности.
7.2. Проекция
Проекцията в MongoDB ни позволява да изберем само полетата, които искаме да извлечем от документи в нашите заявки . В случай, че структурата на документа е сложна и тежка, това може да бъде наистина полезно, когато имаме нужда само от няколко полета.
Да предположим, че трябва само да извлечем книги с тяхното заглавие в нашата заявка:
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());
Тук, както виждаме, връщаме само заглавието в нашия резултат, а не автора и други полета. Трябва обаче да внимаваме при използването на прогнозирания изход при запазване обратно в MongoDB. Това може да доведе до загуба на данни!
7.3. Индексиране
Индексите играят много важна роля в оптимизирането на заявки с бази данни — релационни, както и много нерелационни.
MongoDB дефинира индекси на ниво колекция с уникален индекс, създаден на първичния ключ по подразбиране . Освен това MongoDB позволява създаването на индекси във всяко поле или подполе в рамките на документ. Трябва да изберем да създадем индекс за ключ в зависимост от заявката, която искаме да създадем.
Например, в нашия пример може да пожелаем да създадем индекс в полето „заглавие“ на Книга тъй като в крайна сметка често задаваме запитвания за него:
@Indexes({
@Index(
fields = @Field("title"),
options = @IndexOptions(name = "book_title")
)
})
public class Book {
// ...
@Property
private String title;
// ...
}
Разбира се, можем да предадем допълнителни опции за индексиране, за да приспособим нюансите на индекса, който се създава. Имайте предвид, че полето трябва да бъде анотирано от @Свойство да се използва в индекс.
Освен това, освен индекса на ниво клас, Morphia има анотация за дефиниране и на индекс на ниво поле.
7.4. Проверка на схема
Имаме опция да предоставим правила за валидиране на данни за колекция, която MongoDB може да използва, докато извършва операция за актуализиране или вмъкване . Morphia поддържа това чрез техните API.
Да кажем, че не искаме да вмъкнем книга без валидна цена. Можем да използваме валидирането на схемата, за да постигнем това:
@Validation("{ price : { $gt : 0 } }")
public class Book {
// ...
@Property("price")
private double cost;
// ...
}
Има богат набор от валидации, предоставени от MongoDB, които могат да бъдат използвани тук.
8. Алтернативни MongoDB ODM
Morphia не е единственият наличен MongoDB ODM за Java. Има няколко други, които можем да обмислим да използваме в нашите приложения. Тук не е възможна дискусия за сравнение с Morphia, но винаги е полезно да знаете нашите опции:
- Spring Data:Осигурява базиран на Spring модел за програмиране за работа с MongoDB
- MongoJack:Предоставя директно съпоставяне от JSON към обекти MongoDB
Това не е пълен списък с MongoDB ODM за Java, но има някои интересни алтернативи!