1. 序章

この記事では、 MapDBライブラリ(コレクションのようなAPIを介してアクセスされる組み込みデータベースエンジン)について説明します。

まず、データベースの構成、オープン、および管理に役立つコアクラスDBおよびDBMakerについて説明します。 次に、データを格納および取得するMapDBデータ構造の例をいくつか紹介します。

最後に、MapDBを従来のデータベースおよびJavaコレクションと比較する前に、いくつかのインメモリモードを確認します。

2. MapDBへのデータの保存

まず、このチュートリアル全体で常に使用する2つのクラスを紹介しましょう— DB DBMaker。  DBクラスは、開いているデータベースを表します。 そのメソッドは、データベースレコードを処理するためのストレージコレクションを作成および閉じるためのアクションと、トランザクションイベントを処理するためのアクションを呼び出します。

DBMakerは、データベースの構成、作成、およびオープンを処理します。構成の一部として、データベースをメモリ内またはファイルシステム上でホストすることを選択できます。

2.1. 単純なHashMapの例

これがどのように機能するかを理解するために、メモリ内の新しいデータベースをインスタンス化してみましょう。

まず、DBMakerクラスを使用して新しいインメモリデータベースを作成しましょう。

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

DB オブジェクトが起動して実行されると、それを使用して HTreeMap を構築し、データベースレコードを操作できます。

String welcomeMessageKey = "Welcome Message";
String welcomeMessageString = "Hello Baeldung!";

HTreeMap myMap = db.hashMap("myMap").createOrOpen();
myMap.put(welcomeMessageKey, welcomeMessageString);

HTreeMap は、MapDBのHashMap実装です。 これで、データベースにデータができたので、getメソッドを使用してデータを取得できます。

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

最後に、データベースの使用が終了したので、それ以上の変更を避けるためにデータベースを閉じる必要があります。

db.close();

データをメモリではなくファイルに保存するには、DBオブジェクトのインスタンス化方法を変更するだけです。

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

上記の例では、型パラメーターを使用していません。 その結果、特定のタイプで機能するように結果をキャストすることに固執しています。 次の例では、キャストの必要性を排除するためにシリアライザーを紹介します。

2.2. コレクション

MapDBにはさまざまなコレクションタイプが含まれています。説明のために、 NavigationSet を使用してデータベースにデータを追加および取得します。これは、Java Set

DBオブジェクトの簡単なインスタンス化から始めましょう。

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

次に、NavigationSetを作成しましょう。

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

ここで、シリアライザーは、データベースからの入力データがStringオブジェクトを使用してシリアル化および逆シリアル化されることを保証します。

次に、いくつかのデータを追加しましょう。

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

次に、2つの異なる値がデータベースに正しく追加されていることを確認しましょう。

assertEquals(2, set.size());

最後に、これはセットなので、重複する文字列を追加して、データベースにまだ2つの値しか含まれていないことを確認しましょう。

set.add("Baeldung");

assertEquals(2, set.size());

2.3. トランザクション

従来のデータベースと同様に、 DBクラスは、データベースに追加するデータをコミットおよびロールバックするためのメソッドを提供します。

この機能を有効にするには、DBtransactionEnableメソッドで初期化する必要があります。

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

次に、簡単なセットを作成し、データを追加して、データベースにコミットしましょう。

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

set.add("One");
set.add("Two");

db.commit();

assertEquals(2, set.size());

次に、3番目のコミットされていない文字列をデータベースに追加しましょう。

set.add("Three");

assertEquals(3, set.size());

データに満足できない場合は、DBのロールバックメソッドを使用してデータをロールバックできます。

db.rollback();

assertEquals(2, set.size());

2.4. シリアライザー

MapDBは、コレクション内のデータを処理する多種多様なシリアライザーを提供します。 最も重要な構築パラメーターは、DBオブジェクト内の個々のコレクションを識別する名前です。

HTreeMap<String, Long> map = db.hashMap("indentification_name")
  .keySerializer(Serializer.STRING)
  .valueSerializer(Serializer.LONG)
  .create();

シリアル化をお勧めしますが、これはオプションであり、スキップできます。 ただし、これにより、一般的なシリアル化プロセスが遅くなることに注意してください。

3. HTreeMap

MapDBの HTreeMapは、データベースを操作するためのHashMapおよびHashSetコレクションを提供します。 HTreeMap はセグメント化されたハッシュツリーであり、固定サイズのハッシュテーブルを使用しません。 代わりに、自動拡張インデックスツリーを使用し、テーブルが大きくなるにつれてすべてのデータを再ハッシュすることはありません。 さらに、 HTreeMapはスレッドセーフであり、複数のセグメントを使用した並列書き込みをサポートしています。

まず、キーと値の両方にStringを使用する単純なHashMapをインスタンス化します。

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

HTreeMap<String, String> hTreeMap = db
  .hashMap("myTreeMap")
  .keySerializer(Serializer.STRING)
  .valueSerializer(Serializer.STRING)
  .create();

上記では、キーと値に対して個別のシリアライザーを定義しました。 HashMap が作成されたので、putメソッドを使用してデータを追加しましょう。

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

assertEquals(2, hTreeMap.size());

HashMapObjectのhashCodeメソッドで機能するため、同じキーを使用してデータを追加すると、値が上書きされます。

hTreeMap.put("key1", "value3");

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

4. SortedTableMap

MapDBのSortedTableMapは、キーを固定サイズのテーブルに格納し、検索にバイナリ検索を使用します。 準備が完了すると、マップは読み取り専用になります。

作成とクエリのプロセスを見ていきましょう SortedTableMap。 まず、データを保持するためのメモリマップドボリュームと、データを追加するためのシンクを作成します。 ボリュームの最初の呼び出しで、読み取り専用フラグを false に設定し、ボリュームに書き込めるようにします。

String VOLUME_LOCATION = "sortedTableMapVol.db";

Volume vol = MappedFileVol.FACTORY.makeVolume(VOLUME_LOCATION, false);

SortedTableMap.Sink<Integer, String> sink =
  SortedTableMap.create(
    vol,
    Serializer.INTEGER,
    Serializer.STRING)
    .createFromSink();

次に、データを追加し、シンクでcreateメソッドを呼び出してマップを作成します。

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

sink.create();

マップが存在するので、読み取り専用ボリュームを定義し、SortedTableMapのopenメソッドを使用してマップを開くことができます。

Volume openVol = MappedFileVol.FACTORY.makeVolume(VOLUME_LOCATION, true);

SortedTableMap<Integer, String> sortedTableMap = SortedTableMap
  .open(
    openVol,
    Serializer.INTEGER,
    Serializer.STRING);

assertEquals(100, sortedTableMap.size());

4.1. 二分探索

先に進む前に、SortedTableMapがどのように二分探索を利用するかをより詳細に理解しましょう。

SortedTableMap は、ストレージをページに分割します。各ページには、キーと値で構成される複数のノードが含まれます。 これらのノード内には、Javaコードで定義するキーと値のペアがあります。

SortedTableMap は、正しい値を取得するために3つのバイナリ検索を実行します。

  1. 各ページのキーは、ヒープ上に配列で格納されます。 SortedTableMap は、正しいページを見つけるために二分探索を実行します。
  2. 次に、ノード内のキーごとに解凍が行われます。 二分探索は、キーに従って正しいノードを確立します。
  3. 最後に、 SortedTableMap はノード内のキーを検索して、正しい値を見つけます。

5. インメモリモード

MapDBは、 3種類のメモリ内ストアを提供します。各モードを簡単に見て、その仕組みを理解し、その利点を調べてみましょう。

5.1. オンヒープ

オンヒープモードオブジェクトを単純なJavaコレクションマップに格納します。 これシリアル化を採用しておらず、小さなデータセットでは非常に高速です。 

ただし、データはヒープに格納されるため、データセットはガベージコレクション(GC)によって管理されます。 GCの期間はデータセットのサイズとともに増加し、パフォーマンスが低下します。

オンヒープモードを指定する例を見てみましょう。

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

5.2. バイト[]

2番目のストアタイプはバイト配列に基づいています。 このモードでは、データがシリアル化され、最大1MBのサイズの配列に格納されます。技術的にはヒープ上ですが、この方法はガベージコレクションに効率的です。

これはデフォルトで推奨されており、「HelloBaeldung」の例で使用されています。

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

5.3. DirectByteBuffer

最終ストアはDirectByteBufferに基づいています。Java1.4で導入されたダイレクトメモリにより、Javaヒープではなくネイティブメモリにデータを直接渡すことができます。 その結果、データは完全にオフヒープに格納されます。

このタイプのストアは、次のコマンドで呼び出すことができます。

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

6. なぜMapDBなのか?

では、なぜMapDBを使用するのでしょうか。

6.1. MapDBと従来のデータベース

MapDBは、わずか数行のJavaコードで構成された多数のデータベース機能を提供します。 MapDBを使用すると、プログラムを機能させるために必要な、時間のかかるさまざまなサービスや接続のセットアップを回避できます。

これに加えて、MapDBを使用すると、Javaコレクションに精通したデータベースの複雑さにアクセスできます。 MapDBを使用すると、SQLは不要であり、単純なgetメソッド呼び出しでレコードにアクセスできます。

6.2. MapDBと単純なJavaコレクション

Javaコレクションは、実行が停止すると、アプリケーションのデータを保持しません。 MapDBは、Javaコレクションタイプのユーティリティを維持しながら、アプリケーション内のデータをすばやく簡単に永続化できる、シンプルで柔軟性のあるプラグ可能なサービスを提供します。

7. 結論

この記事では、MapDBの組み込みデータベースエンジンとコレクションフレームワークについて詳しく説明しました。

まず、データベースを構成、オープン、および管理するためのコアクラスDBおよびDBMakerを確認しました。 次に、MapDBがレコードを処理するために提供するデータ構造の例をいくつか見ていきました。 最後に、従来のデータベースまたはJavaコレクションに対するMapDBの利点を確認しました。

いつものように、サンプルコードはGitHubから入手できます。