Panduan untuk MapDB

1. Perkenalan

Dalam artikel ini, kita akan melihat pustaka MapDB - mesin database tertanam yang diakses melalui API seperti koleksi.

Kami mulai dengan menjelajahi kelas inti DB dan DBMaker yang membantu mengkonfigurasi, membuka, dan mengelola database kami. Kemudian, kami akan mempelajari beberapa contoh struktur data MapDB yang menyimpan dan mengambil data.

Terakhir, kita akan melihat beberapa mode dalam memori sebelum membandingkan MapDB dengan database tradisional dan Koleksi Java.

2. Menyimpan Data di MapDB

Pertama, mari perkenalkan dua kelas yang akan kita gunakan terus-menerus sepanjang tutorial ini - DB dan DBMaker. Kelas DB mewakili database terbuka. Metodenya meminta tindakan untuk membuat dan menutup koleksi penyimpanan untuk menangani catatan database, serta menangani peristiwa transaksional.

DBMaker menangani konfigurasi, pembuatan, dan pembukaan database. Sebagai bagian dari konfigurasi, kita dapat memilih untuk meng-host database kita baik di dalam memori atau di sistem file kita.

2.1. Contoh HashMap Sederhana

Untuk memahami cara kerjanya, mari buat database baru di memori.

Pertama, mari buat database baru dalam memori menggunakan kelas DBMaker :

DB db = DBMaker.memoryDB().make();

Setelah objek DB kami aktif dan berjalan, kami dapat menggunakannya untuk membuat HTreeMap agar dapat digunakan dengan catatan database kami:

String welcomeMessageKey = "Welcome Message"; String welcomeMessageString = "Hello Baeldung!"; HTreeMap myMap = db.hashMap("myMap").createOrOpen(); myMap.put(welcomeMessageKey, welcomeMessageString);

HTreeMap adalah implementasi HashMap dari MapDB . Jadi, sekarang kita memiliki data di database kita, kita bisa mengambilnya menggunakan metode get :

String welcomeMessageFromDB = (String) myMap.get(welcomeMessageKey); assertEquals(welcomeMessageString, welcomeMessageFromDB);

Akhirnya, setelah kita selesai dengan database, kita harus menutupnya untuk menghindari mutasi lebih lanjut:

db.close();

Untuk menyimpan data kita dalam sebuah file, bukan dalam memori, yang perlu kita lakukan hanyalah mengubah cara objek DB kita dibuat:

DB db = DBMaker.fileDB("file.db").make();

Contoh kami di atas tidak menggunakan parameter tipe. Akibatnya, kami terjebak dengan mentransmisikan hasil kami untuk bekerja dengan jenis tertentu. Dalam contoh kami berikutnya, kami akan memperkenalkan Serializers untuk menghilangkan kebutuhan casting.

2.2. Koleksi

MapDB mencakup jenis koleksi yang berbeda. Untuk mendemonstrasikan, mari tambahkan dan ambil beberapa data dari database kita menggunakan NavigableSet , yang berfungsi seperti yang Anda harapkan dari Java Set :

Mari kita mulai dengan contoh sederhana dari objek DB kita :

DB db = DBMaker.memoryDB().make();

Selanjutnya, mari buat NavigableSet kami :

NavigableSet set = db .treeSet("mySet") .serializer(Serializer.STRING) .createOrOpen();

Di sini, serializer memastikan bahwa data input dari database kami adalah serial dan deserialisasi menggunakan objek String .

Selanjutnya, mari tambahkan beberapa data:

set.add("Baeldung"); set.add("is awesome");

Sekarang, mari kita periksa apakah dua nilai berbeda kita telah ditambahkan ke database dengan benar:

assertEquals(2, set.size());

Terakhir, karena ini adalah satu set, mari tambahkan string duplikat dan verifikasi bahwa database kita masih hanya berisi dua nilai:

set.add("Baeldung"); assertEquals(2, set.size());

2.3. Transaksi

Sama seperti database tradisional, DB kelas menyediakan metode untuk melakukan dan rollback data yang kita tambahkan ke database kami.

Untuk mengaktifkan fungsi ini, kita perlu menginisialisasi DB kita dengan metode transactionEnable :

DB db = DBMaker.memoryDB().transactionEnable().make();

Selanjutnya, mari buat satu set sederhana, tambahkan beberapa data, dan komit ke database:

NavigableSet set = db .treeSet("mySet") .serializer(Serializer.STRING) .createOrOpen(); set.add("One"); set.add("Two"); db.commit(); assertEquals(2, set.size());

Sekarang, mari tambahkan string ketiga yang tidak terikat ke database kita:

set.add("Three"); assertEquals(3, set.size());

Jika kami tidak puas dengan data kami, kami dapat melakukan rollback data menggunakan metode rollback DB :

db.rollback(); assertEquals(2, set.size());

2.4. Serializer

MapDB menawarkan berbagai macam serializer, yang menangani data di dalam koleksi. Parameter konstruksi yang paling penting adalah nama, yang mengidentifikasi koleksi individu dalam objek DB :

HTreeMap map = db.hashMap("indentification_name") .keySerializer(Serializer.STRING) .valueSerializer(Serializer.LONG) .create();

Meskipun serialisasi direkomendasikan, ini opsional dan dapat dilewati. Namun, perlu dicatat bahwa ini akan menyebabkan proses serialisasi generik yang lebih lambat.

3. HTreeMap

MapDB's HTreeMap provides HashMap and HashSet collections for working with our database. HTreeMap is a segmented hash tree and does not use a fixed-size hash table. Instead, it uses an auto-expanding index tree and does not rehash all of its data as the table grows. To top it off, HTreeMap is thread-safe and supports parallel writes using multiple segments.

To begin, let's instantiate a simple HashMap that uses String for both keys and values:

DB db = DBMaker.memoryDB().make(); HTreeMap hTreeMap = db .hashMap("myTreeMap") .keySerializer(Serializer.STRING) .valueSerializer(Serializer.STRING) .create();

Above, we've defined separate serializers for the key and the value. Now that our HashMap is created, let's add data using the put method:

hTreeMap.put("key1", "value1"); hTreeMap.put("key2", "value2"); assertEquals(2, hTreeMap.size());

As HashMap works on an Object's hashCode method, adding data using the same key causes the value to be overwritten:

hTreeMap.put("key1", "value3"); assertEquals(2, hTreeMap.size()); assertEquals("value3", hTreeMap.get("key1"));

4. SortedTableMap

MapDB's SortedTableMap stores keys in a fixed-size table and uses binary search for retrieval. It's worth noting that once prepared, the map is read-only.

Let's walk through the process of creating and querying a SortedTableMap. We'll start by creating a memory-mapped volume to hold the data, as well as a sink to add data. On the first invocation of our volume, we'll set the read-only flag to false, ensuring we can write to the volume:

String VOLUME_LOCATION = "sortedTableMapVol.db"; Volume vol = MappedFileVol.FACTORY.makeVolume(VOLUME_LOCATION, false); SortedTableMap.Sink sink = SortedTableMap.create( vol, Serializer.INTEGER, Serializer.STRING) .createFromSink();

Next, we'll add our data and call the create method on the sink to create our map:

for(int i = 0; i < 100; i++){ sink.put(i, "Value " + Integer.toString(i)); } sink.create();

Now that our map exists, we can define a read-only volume and open our map using SortedTableMap's open method:

Volume openVol = MappedFileVol.FACTORY.makeVolume(VOLUME_LOCATION, true); SortedTableMap sortedTableMap = SortedTableMap .open( openVol, Serializer.INTEGER, Serializer.STRING); assertEquals(100, sortedTableMap.size());

4.1. Binary Search

Before we move on, let's understand how the SortedTableMap utilizes binary search in more detail.

SortedTableMap splits the storage into pages, with each page containing several nodes comprised of keys and values. Within these nodes are the key-value pairs that we define in our Java code.

SortedTableMap performs three binary searches to retrieve the correct value:

  1. Keys for each page are stored on-heap in an array. The SortedTableMap performs a binary search to find the correct page.
  2. Next, decompression occurs for each key in the node. A binary search establishes the correct node, according to the keys.
  3. Finally, the SortedTableMap searches over the keys within the node to find the correct value.

5. In-Memory Mode

MapDB offers three types of in-memory store. Let's take a quick look at each mode, understand how it works, and study its benefits.

5.1. On-Heap

The on-heap mode stores objects in a simple Java Collection Map. It does not employ serialization and can be very fast for small datasets.

However, since the data is stored on-heap, the dataset is managed by garbage collection (GC). The duration of GC rises with the size of the dataset, resulting in performance drops.

Let's see an example specifying the on-heap mode:

DB db = DBMaker.heapDB().make();

5.2. Byte[]

The second store type is based on byte arrays. In this mode, data is serialized and stored into arrays up to 1MB in size. While technically on-heap, this method is more efficient for garbage collection.

This is recommended by default, and was used in our ‘Hello Baeldung' example:

DB db = DBMaker.memoryDB().make();

5.3. DirectByteBuffer

The final store is based on DirectByteBuffer. Direct memory, introduced in Java 1.4, allows the passing of data directly to native memory rather than Java heap. As a result, the data will be stored completely off-heap.

We can invoke a store of this type with:

DB db = DBMaker.memoryDirectDB().make();

6. Why MapDB?

So, why use MapDB?

6.1. MapDB vs Traditional Database

MapDB offers a large array of database functionality configured with just a few lines of Java code. When we employ MapDB, we can avoid the often time-consuming setup of various services and connections needed to get our program to work.

Beyond this, MapDB allows us to access the complexity of a database with the familiarity of a Java Collection. With MapDB, we do not need SQL, and we can access records with simple get method calls.

6.2. Koleksi MapDB vs Java Sederhana

Koleksi Java tidak akan menyimpan data aplikasi kita setelah berhenti dijalankan. MapDB menawarkan layanan sederhana, fleksibel, dan dapat dicolokkan yang memungkinkan kami menyimpan data dengan cepat dan mudah dalam aplikasi kami sambil mempertahankan utilitas jenis pengumpulan Java.

7. Kesimpulan

Pada artikel ini, kami telah mempelajari lebih dalam tentang mesin database yang disematkan dan kerangka pengumpulan MapDB.

Kami mulai dengan melihat kelas inti DB dan DBMaker untuk mengkonfigurasi, membuka, dan mengelola database kami. Kemudian, kami menelusuri beberapa contoh struktur data yang ditawarkan MapDB untuk bekerja dengan catatan kami. Akhirnya, kami melihat keunggulan MapDB dibandingkan database tradisional atau Java Collection.

Seperti biasa, kode contoh tersedia di GitHub.