はじめに

システム開発でよく使われるデータベースといえば、RDB(リレーショナルデータベース)が定番です。私のチームでも長らく、Oracle や MySQL などの RDB を利用してきましたが、ある新規APIの開発で MongoDB を採用してみたところ、思っていた以上に開発がスムーズになりました。

この記事では、MongoDB を Java + Spring Boot で使用してみて感じた3つのメリットを、実際の実装例を交えて紹介します。

MongoDB の特徴

MongoDB はドキュメント指向の NoSQL データベースであり、RDB と比べて非常に緩い設計になっているのが特徴です。RDBと違いを感じるのは、例えば以下のような点です。

  • 構造化オブジェクト(JSONのようなもの)を丸ごと保存できる
  • 格納するオブジェクトの仕様を厳密に決めておく必要が無い(カラム定義などは不要)
  • コレクション(RDBでいうところのテーブル)に追加しようとしたときに、コレクションが無ければ自動的に作成される。データベースが無かった場合は、データベースも自動的に作成される

プログラムから 何でも入る入れ物 的な感覚で、簡単に使えるのが特徴です。

簡単に始められる MongoDB

MongoDB の準備

MongoDB はローカルにインストールして起動します。インストール手順は割愛しますが、Dockerイメージ を使うか、公式サイトからダウンロードしてインストールすることになります。

前述した通り、データベースやコレクションを用意する必要は無いのですが、ユーザだけは用意しておく必要があります。インストールした後、以下のコマンドを MongoDB Shell などで実行します。

use admin
db.createUser({
  user: "appuser",
  pwd:  "secret",
  roles: [ { role: "readWrite", db: "sampledb" } ]
})

Spring Boot の設定

Spring Boot では、spring-boot-starter-data-mongodb を依存関係に追加するだけで、 MongoDB を簡単に利用できます。Maven の場合は以下を pom.xml に追加します。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

接続設定は Spring Boot の application.yml に以下のように記述します。

spring:
  data:
    mongodb:
      host: localhost
      port: 27017
      database: sampledb
      username: appuser
      password: secret
      auto-index-creation: true

auto-index-creation は、後に説明する @Indexed でインデックスを作る機能を有効にするための設定です。

エンティティ

@Document(collection = "login_history")
public class LoginHistory {
    @Id
    private String id;

    @Indexed
    private String userId;

    @Indexed(expireAfter = "30d") // 30日で自動削除
    private LocalDateTime createdAt;
}

エンティティクラスに付けている @Document アノテーションで、保存するコレクション名を指定しています。

@Id をつけたフィールドは _id という特殊なフィールドにマッピングされます。これはコレクション内でドキュメントを一意に識別するための値で、RDBでいうところの主キーに相当する役割を持ちます。

@Indexed は項目にインデックスを作成するアノテーションです。RDBのインデックスと同様で、この項目による検索を高速に行えるようになります。

expireAfter をつけると、 TTL(Time To Live)インデックス という特殊なインデックスが作成されます。これは、設定した期間を過ぎると、MongoDBが自動的にレコードを削除してくれる機能です。この例では createdAt から30日以上経過している場合に削除対象となります。

リポジトリ

public interface LoginHistoryRepository extends MongoRepository<LoginHistory, String> {
    List<LoginHistory> findByUserId(String userId);
}

あとはこのリポジトリをDIして、簡単に読み書きができます。

// 保存
loginHistory.setId("a001");
loginHistory.setUserId("sampleUser");
loginHistory.setCreatedAt(java.time.LocalDateTime.now());
loginHistoryRepository.save(loginHistory);

// IDで取得
Optional<LoginHistory> h1 = loginHistoryRepository.findById("a001");

// ユーザIDで取得
List<LoginHistory> histories = loginHistoryRepository.findByUserId("sampleUser");

このようなコードで最初のレコードを save すると、MongoDB 側には自動的に login_history コレクションが作成され、userIdcreatedAt にインデックスが付与されます。

実感した MongoDB の3つのメリット

1. DDL作成や実行が不要

前述した通り、MongoDB はコレクションが無くても自動的に作ってくれますし、スキーマを決めておく必要もありません。

そのため、Spring のプログラムから何かを保存したいと思ったときに、エンティティとリポジトリさえ用意すれば、すぐに保存することができます。

これによって、開発者がプログラムとDBを同時に扱えるようになり、RDBと比べて開発の自由度とスピードが上がりました。

2. TTLインデックスによる自動削除が便利

ログやセッション情報など、「一定期間で削除してよいデータ」は多く存在します。RDBでは古いデータを削除するためにバッチ処理を用意し、スケジュール実行・監視を行うのが一般的です。

MongoDBでは、前述したTTLインデックスを設定するだけで、MongoDBが自動的に期限切れのデータを削除してくれるため、削除バッチを作る必要がなくなりました。

3. データ構造が柔軟で変更に強い

正規化せずにデータを格納するため、RDBでは複数テーブルに分けてJOINしていたデータも1ドキュメントにまとめて保持できます。

そのため、データ構造が変更になっても、RDBと比べて影響が少なく、変更に強いと感じました。

導入して感じたこと

MongoDB はとても使いやすくて便利なデータベースですが、必ずしもRDBよりも優れているというわけではありません。向き不向きはあります。

具体的には、やはり正規化されていないので、複雑な結合クエリや集計は書きづらいです。複雑な検索が必要なデータには、RDBのほうが向いていると思います。

一方で、複雑な集計を必要としないデータであれば、非常に使いやすく、用途によっては良い選択肢になると感じました。

まとめ

MongoDB を Java/Spring 環境で使ってみて感じたメリットは、以下の3点でした。

  • DDL作成や実行が不要
  • TTLインデックスによる自動削除が便利
  • データ構造が柔軟で変更に強い

一昔前は、データベースと言えばRDBでしたが、最近は MongoDB のようなドキュメント指向DBを始めとして、様々な種類のデータベースが登場しています。そして、それらが現場で使われ始めているとも感じています。

データベースには向き不向きがあります。特定の技術に固執するのではなく、様々な種類のデータベースの特徴を比較検討し、最適なものを選択することが、これからの開発には求められていくと思います。