Morphiaの概要– MongoDB向けJava ODM

1. 概要

このチュートリアルでは、JavaでのMongoDBのオブジェクトドキュメントマッパー(ODM)であるhttps://morphia.dev/[Morphia]の使用方法を理解します。
その過程で、ODMとは何か、それがMongoDBとの連携をどのように促進するかについても理解します。

2. ODMとは何ですか?

この分野で未経験の人のために、* https://www.mongodb.com/ [MongoDB]は、自然に配布されるように構築されたドキュメント指向のデータベースです*。 ドキュメント指向データベースは、簡単に言えば、ドキュメントを管理します。これは、*半構造化データを整理するスキーマレスな方法*にすぎません。 これらは、SQLデータベースの従来の組織からの明らかな出発にちなんで名付けられた、より広く、大まかに定義されたNoSQLデータベースの傘下に入ります。
MongoDBはlink:/java-mongodb[*Javaのようなほとんどすべての一般的なプログラミング言語のドライバー*]を提供します。 これらのドライバーは、MongoDBを操作するための抽象化レイヤーを提供するため、Wire Protocolを直接操作することはありません。 これは、Oracleがリレーショナルデータベース用のJDBCドライバーの実装を提供していると考えてください。
ただし、JDBCを直接使用していた日々を思い出すと、特にオブジェクト指向のパラダイムでは、JDBCがいかに厄介になるかを理解できます。 幸いなことに、Hibernateのようなオブジェクトリレーショナルマッピング(ORM)フレームワークが用意されています。 MongoDBでもそれほど違いはありません。
確かに低レベルのドライバーで作業できますが、タスクを完了するにはさらに多くの定型文が必要です。 ここには、Object Document Mapper(ODM)と呼ばれるORMと同様の概念があります。 MorphiaはJavaプログラミング言語のスペースを正確に埋め、MongoDBのJavaドライバーの上で動作します。

3. 依存関係のセットアップ

いくつかのコードを理解するのに十分な理論を見てきました。 例として、書籍のライブラリをモデル化し、Morphiaを使用してMongoDBで管理する方法を確認します。
しかし、始める前に、いくつかの依存関係をセットアップする必要があります。

3.1. MongoDB

使用するには、MongoDBの実行中のインスタンスが必要です。 これを取得する方法はいくつかありますが、最も簡単な方法は、ローカルマシンでhttps://docs.mongodb.com/v3.2/administration/install-community/ [コミュニティエディションをダウンロードしてインストール]することです。
MongoDBが実行されるポートを含む、すべてのデフォルト構成をそのままにしておく必要があります。

3.2. モルフィア

Morphia用のビルド済みJARをhttps://search.maven.org/search?q=g:dev.morphia.morphia%20AND%20a:core[Maven Central]からダウンロードして、Javaプロジェクトで使用できます。
ただし、最も簡単な方法は、Mavenのような依存関係管理ツールを使用することです。
<dependency>
    <groupId>dev.morphia.morphia</groupId>
    <artifactId>core</artifactId>
    <version>1.5.3</version>
</dependency>

4. Morphiaを使用して接続する方法は?

MongoDBをインストールして実行し、JavaプロジェクトにMorphiaをセットアップしたので、Morphiaを使用してMongoDBに接続する準備ができました。
それをどのように達成できるか見てみましょう。
Morphia morphia = new Morphia();
morphia.mapPackage("com.baeldung.morphia");
Datastore datastore = morphia.createDatastore(new MongoClient(), "library");
datastore.ensureIndexes();
それはほとんどそれです! これをよく理解しましょう。 マッピング操作を機能させるには、2つのことが必要です。
  1. マッパー:これは、Java POJOをMongoDBに*マッピングする役割を果たします。
    コレクション*。 上記のコードスニペットでは、_Morphia_がそれを担当するクラスです。 POJOを検索するパッケージの設定方法に注意してください。

  2. 接続:これは、MongoDBデータベースへの接続です。
    マッパーはさまざまな操作を実行できます。 クラス_Datastore_は、パラメーターとして_MongoClient_のインスタンス(Java MongoDBドライバーから)およびMongoDBデータベースの名前を取り、動作するアクティブな接続を返します

    したがって、この_Datastore_を使用してエンティティを操作するように設定されています。

5. エンティティを操作する方法は?

新しく作成した_Datastore_を使用する前に、作業するいくつかのドメインエンティティを定義する必要があります。

5.1. 単純なエンティティ

まず、いくつかの属性を持つ単純な_Book_エンティティを定義することから始めましょう。
@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に限定するアノテーション* @ _ Entity_に注意してください。
    Morphiaによるマッピング*

  • Morphiaは、デフォルトで、エンティティをMongoDBのコレクションにマッピングします。
    クラスの名前ですが、これを明示的にオーバーライドできます(エンティティ_Book_ hereで行ったように)

  • Morphiaはデフォルトで、エンティティの変数をのキーにマッピングします
    変数の名前によるMongoDBコレクションですが、これもオーバーライドできます(変数_cost_ hereで行ったように)

  • 最後に、エンティティ内の変数をマークして、
    アノテーション@ Id *による主キー(ここで私たちの本にISBNを使用しているように)

5.2. 関係を持つエンティティ

ただし、現実の世界では、エンティティは見た目ほど単純ではなく、相互に複雑な関係を持っています。 たとえば、単純なエンティティ_Book_は_Publisher_を持つことができ、他のコンパニオンブックを参照できます。 それらをどのようにモデル化するのですか?
MongoDBは、リレーションシップを構築するための2つのメカニズム–参照と埋め込み*を提供します。 名前が示すように、参照を使用すると、MongoDBは関連データを同じコレクションまたは別のコレクションに個別のドキュメントとして保存し、そのIDを使用して単に参照します。
それどころか、埋め込みでは、MongoDBは親ドキュメント自体にリレーションを保存するか、むしろ埋め込みます。
それらの使用方法を見てみましょう。 _Book_に_Publisher_を埋め込むことから始めましょう。
@Embedded
private Publisher publisher;
とても簡単です。 それでは、他の本への参照を追加してみましょう。
@Reference
private List<Book> companionBooks;
それだけです-Morphiaは、MongoDBでサポートされているように、モデルの関係に便利な注釈を提供します。 ただし、*参照と埋め込みの選択は、データモデルの複雑さ、冗長性、一貫性*を考慮すべき点です。
この演習は、リレーショナルデータベースの正規化に似ています。
これで、_Datastore_を使用して_Book_に対していくつかの操作を実行する準備が整いました。

6. 基本的な操作

Morphiaを使用していくつかの基本的な操作を行う方法を見てみましょう。

6.1. Save

最も単純な操作から始めましょう。MongoDBデータベース_library_に_Book_のインスタンスを作成します。
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データベースにコレクションを作成し(存在しない場合)、アップサート操作を実行できるようになります。

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はさらに多くのhttps://morphia.dev/1.5/guides/querying/ [フィルターと演算子を使用した複雑なクエリ構築]をサポートしています。 さらに、Morphiaでは、クエリの結果を制限、スキップ、および順序付けできます。
さらに、Morphiaを使用すると、MongoDBのJavaドライバーで作成された生のクエリを使用して、必要に応じて制御を強化できます。

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());
ここでは、クエリと更新操作を作成して、クエリによって返されるすべての書籍の価格を1つずつ増やしています。

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を使用してすべてを実行することはできませんが、その一部を実現することは確かに可能です。 他の人にとっては、悲しいことに、MongoDBのJavaドライバーにフォールバックする必要があります。
Morphiaで実行できるこれらの高度な操作のいくつかに注目しましょう。

7.1. 集約

MongoDBの集約により、一連のドキュメントを操作し、集約された出力を生成できるパイプラインでの一連の操作を定義できます。
Morphiaには、このような集約パイプラインをサポートするAPIがあります。
すべての書籍が著者によってグループ化されるように、ライブラリデータを集約したいとします。
Iterator<Author> iterator = datastore.createAggregation(Book.class)
  .group("author", grouping("books", push("title")))
  .out(Author.class);
それで、これはどのように機能しますか? 同じ古い_Datastore_を使用して集約パイプラインを作成することから始めます。 ここでは、_Book_など、集計操作を実行するエンティティを提供する必要があります。
次に、ドキュメントを「著者」別にグループ化し、「books」というキーの下に「title」を集約します。 最後に、ここでODMを使用しています。 したがって、集約データを収集するエンティティを定義する必要があります。この場合は、_Author_です。
もちろん、booksという変数を使用して_Author_というエンティティを定義する必要があります。
@Entity
public class Author {
    @Id
    private String name;
    private List<String> books;
    // other necessary getters and setters
}
もちろん、これは、MongoDBおよびhttps://docs.mongodb.com/manual/core/aggregation-pipeline/によって提供される非常に強力な構造の表面をほんの少し引っ掻いただけです[詳細については、さらに詳しく調べることができます]。

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では、ドキュメント内の任意のフィールドまたはサブフィールドにインデックスを作成できます。 作成するクエリに応じて、キーにインデックスを作成することを選択する必要があります。
たとえば、この例では、_Book_の「title」フィールドにインデックスを作成したい場合があります。
@Indexes({
  @Index(
    fields = @Field("title"),
    options = @IndexOptions(name = "book_title")
  )
})
public class Book {
    // ...
    @Property
    private String title;
    // ...
}
もちろん、追加のインデックスオプションを渡して、作成されるインデックスのニュアンスを調整することができます。 インデックスで使用するには、フィールドに@_Property_の注釈を付ける必要があることに注意してください。
さらに、クラスレベルのインデックスとは別に、Morphiaにはフィールドレベルのインデックスも定義する注釈があります。

7.4. スキーマ検証

更新または挿入操作の実行中にMongoDBが使用できるコレクションのデータ検証ルールを提供するオプションがあります*。 Morphiaは、APIを通じてこれをサポートしています。
有効な価格のない本を挿入したくないとしましょう。 スキーマ検証を活用してこれを実現できます。
@Validation("{ price : { $gt : 0 } }")
public class Book {
    // ...
    @Property("price")
    private double cost;
    // ...
}
ここで使用できるhttps://docs.mongodb.com/manual/core/schema-validation/index.html[MongoDBが提供する豊富な検証セット]があります。

8. 代替MongoDB ODM

Java用のMongoDB ODMはMorphiaだけではありません。 私たちのアプリケーションで使用することを検討できる他のいくつかがあります。 モルフィアとの比較に関する議論はここでは不可能ですが、私たちのオプションを知ることは常に役に立ちます。
  • Spring Data
    MongoDBを操作するためのSpringベースのプログラミングモデルを提供します

  • MongoJack:JSONからの直接マッピングを提供します
    MongoDBオブジェクトへ

    これはJava用のMongoDB ODMの完全なリストではありませんが、いくつかの興味深い代替が利用可能です!

9. 結論

この記事では、MongoDBの基本的な詳細と、Javaなどのプログラミング言語からMongoDBに接続して操作するためのODMの使用について理解しました。 さらに、MorphiaをJava用のMongoDB ODMおよびそのさまざまな機能として検討しました。
いつものように、コードはhttps://github.com/eugenp/tutorials/tree/master/persistence-modules/java-mongodb[GitHubで]にあります。