Pengantar Morphia - Java ODM untuk MongoDB

1. Ikhtisar

Dalam tutorial ini, kita akan memahami bagaimana menggunakan Morphia, sebuah Object Document Mapper (ODM) untuk MongoDB di Java.

Dalam prosesnya, kami juga akan memahami apa itu ODM dan bagaimana hal itu memfasilitasi kerja dengan MongoDB.

2. Apa itu ODM ?

Bagi mereka yang belum tahu di bidang ini, MongoDB adalah database berorientasi dokumen yang dibangun untuk didistribusikan secara alami . Database berorientasi dokumen, dalam istilah sederhana, mengelola dokumen, yang tidak lain adalah cara tanpa skema untuk mengatur data semi-terstruktur . Mereka berada di bawah payung database NoSQL yang lebih luas dan didefinisikan secara longgar, dinamai berdasarkan penyimpangan mereka yang jelas dari organisasi tradisional database SQL.

MongoDB menyediakan driver untuk hampir semua bahasa pemrograman populer seperti Java . Driver ini menawarkan lapisan abstraksi untuk bekerja dengan MongoDB sehingga kami tidak bekerja dengan Wire Protocol secara langsung. Anggap ini sebagai Oracle yang menyediakan implementasi driver JDBC untuk database relasional mereka.

Namun, jika kita mengingat kembali hari-hari kita bekerja dengan JDBC secara langsung, kita dapat menghargai betapa berantakannya hal itu - terutama dalam paradigma berorientasi objek. Untungnya, kami memiliki kerangka kerja Pemetaan Relasional Objek (ORM) seperti Hibernate untuk menyelamatkan kami. Ini tidak terlalu berbeda untuk MongoDB.

Meskipun kami dapat bekerja dengan driver level rendah, itu membutuhkan lebih banyak boilerplate untuk menyelesaikan tugasnya. Di sini, kami memiliki konsep yang mirip dengan ORM yang disebut Object Document Mapper (ODM) . Morphia benar-benar mengisi ruang itu untuk bahasa pemrograman Java dan bekerja di atas driver Java untuk MongoDB.

3. Menyiapkan Dependensi

Kami telah melihat cukup teori untuk memasukkan kami ke dalam beberapa kode. Untuk contoh kami, kami akan membuat model perpustakaan buku dan melihat bagaimana kami dapat mengelolanya di MongoDB menggunakan Morphia.

Tetapi sebelum kita mulai, kita perlu menyiapkan beberapa dependensi.

3.1. MongoDB

Kita perlu memiliki instance MongoDB yang sedang berjalan untuk bekerja. Ada beberapa cara untuk mendapatkannya, dan yang paling sederhana adalah mengunduh dan menginstal edisi komunitas di komputer lokal kami.

Kita harus membiarkan semua konfigurasi default apa adanya, termasuk port tempat MongoDB berjalan.

3.2. Morfin

Kami dapat mengunduh JAR yang telah dibuat sebelumnya untuk Morphia dari Maven Central dan menggunakannya dalam proyek Java kami.

Namun, cara termudah adalah dengan menggunakan alat manajemen ketergantungan seperti Maven:

 dev.morphia.morphia core 1.5.3 

4. Bagaimana Hubungkan Menggunakan Morphia?

Sekarang setelah kami menginstal dan menjalankan MongoDB dan telah menyiapkan Morphia di proyek Java kami, kami siap untuk terhubung ke MongoDB menggunakan Morphia.

Mari kita lihat bagaimana kita bisa mencapai itu:

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

Cukup banyak itu! Mari kita pahami ini lebih baik. Kami membutuhkan dua hal agar operasi pemetaan kami berfungsi:

  1. A Mapper: Ini bertanggung jawab untuk memetakan Java POJO kami ke Koleksi MongoDB . Dalam potongan kode kami di atas, Morphia adalah kelas yang bertanggung jawab untuk itu. Perhatikan bagaimana kami mengonfigurasi paket tempat ia harus mencari POJO kami.
  2. A Connection: Ini adalah koneksi ke database MongoDB di mana mapper dapat menjalankan operasi yang berbeda. Kelas Datastore mengambil sebagai parameter instance MongoClient (dari driver Java MongoDB) dan nama database MongoDB, mengembalikan koneksi aktif untuk digunakan .

Jadi, kami siap untuk menggunakan Datastore ini dan bekerja dengan entitas kami.

5. Bagaimana Bekerja dengan Entitas?

Sebelum kita dapat menggunakan Datastore yang baru dibuat , kita perlu menentukan beberapa entitas domain untuk digunakan.

5.1. Entitas Sederhana

Mari kita mulai dengan mendefinisikan entitas Buku sederhana dengan beberapa atribut:

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

Ada beberapa hal menarik yang perlu diperhatikan di sini:

  • Perhatikan anotasi @ Entitas yang membuat POJO ini memenuhi syarat untuk pemetaan ODM oleh Morphia
  • Morphia, secara default, memetakan entitas ke koleksi di MongoDB dengan nama kelasnya, tetapi kami dapat secara eksplisit menimpa ini (seperti yang telah kami lakukan untuk entitas Buku di sini)
  • Morphia, secara default, memetakan variabel dalam entitas ke kunci dalam koleksi MongoDB dengan nama variabel, tetapi sekali lagi kita dapat menimpa ini (seperti yang telah kita lakukan untuk biaya variabel di sini)
  • Terakhir, kita perlu menandai variabel dalam entitas untuk bertindak sebagai kunci utama dengan anotasi @ Id (seperti kita menggunakan ISBN untuk buku kita di sini)

5.2. Entitas dengan Hubungan

Di dunia nyata, entitas tidak sesederhana yang terlihat dan memiliki hubungan yang kompleks satu sama lain. Misalnya, Buku entitas sederhana kami dapat memiliki Penerbit dan dapat merujuk ke buku pendamping lainnya. Bagaimana kita memodelkannya?

MongoDB menawarkan dua mekanisme untuk membangun hubungan - Mereferensikan dan Menyematkan . Seperti namanya, dengan referensi, MongoDB menyimpan data terkait sebagai dokumen terpisah dalam koleksi yang sama atau berbeda dan hanya mereferensikannya menggunakan id-nya.

Sebaliknya, dengan penyematan, MongoDB menyimpan atau lebih tepatnya menyematkan relasi di dalam dokumen induk itu sendiri.

Mari kita lihat bagaimana kita bisa menggunakannya. Mari kita mulai dengan menanamkan Penerbit dalam Buku kita :

@Embedded private Publisher publisher;

Cukup sederhana. Sekarang mari kita lanjutkan dan tambahkan referensi ke buku lain:

@Reference private List companionBooks;

Itu saja - Morphia memberikan anotasi yang nyaman untuk memodelkan hubungan seperti yang didukung oleh MongoDB. Pilihan referensi vs embedding, bagaimanapun, harus diambil dari kompleksitas model data, redundansi, dan konsistensi di antara pertimbangan lainnya.

Latihan ini mirip dengan normalisasi dalam database relasional.

Sekarang, kami siap melakukan beberapa operasi pada Book menggunakan Datastore .

6. Beberapa Operasi Dasar

Mari kita lihat bagaimana bekerja dengan beberapa operasi dasar menggunakan Morphia.

6.1. Menyimpan

Mari kita mulai dengan operasi yang paling sederhana, membuat instance Book di perpustakaan database MongoDB kami :

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

Ini cukup untuk membiarkan Morphia membuat koleksi di database MongoDB kami, jika tidak ada, dan melakukan operasi upsert.

6.2. Pertanyaan

Mari kita lihat apakah kita dapat meminta buku yang baru kita buat di MongoDB:

List books = datastore.createQuery(Book.class) .field("title") .contains("Learning Java") .find() .toList(); assertEquals(1, books.size()); assertEquals(book, books.get(0));

Membuat kueri dokumen di Morphia dimulai dengan membuat kueri menggunakan Datastore dan kemudian menambahkan filter secara deklaratif, untuk menyenangkan mereka yang menyukai pemrograman fungsional!

Morphia mendukung konstruksi kueri yang jauh lebih kompleks dengan filter dan operator. Selain itu, Morphia memungkinkan untuk membatasi, melewatkan, dan mengurutkan hasil dalam kueri.

Terlebih lagi, Morphia memungkinkan kita menggunakan kueri mentah yang ditulis dengan driver Java untuk MongoDB untuk kontrol lebih, jika itu diperlukan.

6.3. Memperbarui

Meskipun operasi penyimpanan dapat menangani pembaruan jika kunci utama cocok, Morphia menyediakan cara untuk memperbarui dokumen secara selektif:

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

Di sini, kami membuat kueri dan operasi pembaruan untuk menaikkan satu harga semua buku yang dikembalikan oleh kueri.

6.4. Menghapus

Akhirnya, yang sudah dibuat harus dihapus! Sekali lagi, dengan Morphia, ini cukup intuitif:

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

Kami membuat kueri sangat mirip seperti sebelumnya dan menjalankan operasi penghapusan di Datastore .

7. Penggunaan Lanjutan

MongoDB memiliki beberapa operasi lanjutan seperti Agregasi, Pengindeksan, dan banyak lainnya . Meskipun tidak mungkin untuk melakukan semua itu menggunakan Morphia, itu pasti mungkin untuk mencapai beberapa dari itu. Untuk yang lainnya, sayangnya, kita harus kembali ke driver Java untuk MongoDB.

Let's focus on some of these advanced operations that we can perform through Morphia.

7.1. Aggregation

Aggregation in MongoDB allows us to define a series of operations in a pipeline that can operate on a set of documents and produce aggregated output.

Morphia has an API to support such an aggregation pipeline.

Let's assume we wish to aggregate our library data in such a manner that we have all the books grouped by their author:

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

So, how does this work? We begin by creating an aggregation pipeline using the same old Datastore. We have to provide the entity on which we wish to perform aggregation operations, for instance, Book here.

Next, we want to group documents by “author” and aggregate their “title” under a key called “books”. Finally, we're working with an ODM here. So, we have to define an entity to collect our aggregated data — in our case, it's Author.

Of course, we have to define an entity called Author with a variable called books:

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

This, of course, just scratches the surface of a very powerful construct provided by MongoDB and can be explored further for details.

7.2. Projection

Projection in MongoDB allows us to select only the fields we want to fetch from documents in our queries. In case document structure is complex and heavy, this can be really useful when we need only a few fields.

Let's suppose we only need to fetch books with their title in our query:

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

Here, as we can see, we only get back the title in our result and not the author and other fields. We should, however, be careful in using the projected output in saving back to MongoDB. This may result in data loss!

7.3. Indexing

Indexes play a very important role in query optimization with databases — relational as well as many non-relational ones.

MongoDB defines indexes at the level of the collection with a unique index created on the primary key by default. Moreover, MongoDB allows indexes to be created on any field or sub-field within a document. We should choose to create an index on a key depending on the query we wish to create.

For instance, in our example, we may wish to create an index on the field “title” of Book as we often end up querying on it:

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

Of course, we can pass additional indexing options to tailor the nuances of the index that gets created. Note that the field should be annotated by @Property to be used in an index.

Moreover, apart from the class-level index, Morphia has an annotation to define a field-level index as well.

7.4. Schema Validation

We've got an option to provide data validation rules for a collection that MongoDB can use while performing an update or insert operation. Morphia supports this through their APIs.

Let's say that we don't want to insert a book without a valid price. We can leverage schema validation to achieve this:

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

There is a rich set of validations provided by MongoDB that can be employed here.

8. Alternative MongoDB ODMs

Morphia bukan satu-satunya MongoDB ODM yang tersedia untuk Java. Ada beberapa lainnya yang dapat kami pertimbangkan untuk digunakan dalam aplikasi kami. Diskusi tentang perbandingan dengan Morphia tidak mungkin dilakukan di sini, tetapi selalu berguna untuk mengetahui pilihan kami:

  • Spring Data: Menyediakan model pemrograman berbasis Spring untuk bekerja dengan MongoDB
  • MongoJack: Menyediakan pemetaan langsung dari JSON ke objek MongoDB

Ini bukan daftar lengkap MongoDB ODMs untuk Java, tetapi ada beberapa alternatif menarik yang tersedia!

9. Kesimpulan

Pada artikel ini, kami memahami detail dasar MongoDB dan penggunaan ODM untuk menghubungkan dan mengoperasikan MongoDB dari bahasa pemrograman seperti Java. Kami lebih jauh menjelajahi Morphia sebagai MongoDB ODM untuk Java dan berbagai kemampuan yang dimilikinya.

Seperti biasa, kode dapat ditemukan di GitHub.