1. 概要

MongoDBデータベースに挿入したばかりのドキュメントのIDが必要になる場合があります。 たとえば、呼び出し元への応答としてIDを送り返したり、デバッグのために作成されたオブジェクトをログに記録したりすることができます。

このチュートリアルでは、MongoDBでIDがどのように実装されているか、およびJavaプログラムを介してコレクションに挿入したばかりのドキュメントのIDを取得する方法を確認します。

2. MongoDBドキュメントのIDは何ですか?

すべてのデータストレージシステムと同様に、MongoDBには、コレクションに保存されているドキュメントごとに一意の識別子が必要です。 この識別子は、リレーショナルデータベースの主キーに相当します。

MongoDBでは、このIDは12バイトで構成されています。

  • 4バイトのタイムスタンプ値は、Unixエポックからの秒数を表します
  • プロセスごとに1回生成される5バイトのランダム値。 このランダムな値は、マシンとプロセスに固有です。
  • 3バイトのインクリメントカウンター

IDは_idという名前のフィールドに格納され、クライアントによって生成されます。これは、ドキュメントをデータベースに送信する前にIDを生成する必要があることを意味します。 クライアント側では、ドライバーで生成されたIDを使用するか、カスタムIDを生成することができます。

同じクライアントによって同じ秒で作成されたドキュメントには、最初の9バイトが共通していることがわかります。 したがって、この場合、IDの一意性はカウンターに依存します。 カウンターを使用すると、クライアントは同じ秒で1,600万を超えるドキュメントを作成できます。

タイムスタンプで始まりますが、識別子がソート基準として使用されないように注意する必要があります。 これは、カウンターが単調であることが保証されていないため、同じ秒で作成されたドキュメントが作成日でソートされることが保証されていないためです。 また、クライアントが異なれば、システムクロックも異なる場合があります。

Javaドライバーは、カウンターに乱数ジェネレーターを使用しますが、これは単調ではありません。 そのため、作成日で並べ替えるためにドライバーで生成されたIDを使用しないでください。

3. ObjectIdクラス

一意の識別子はObjectIdクラスに格納され、手動で解析せずにIDに格納されたデータを取得するための便利なメソッドを提供します。

たとえば、IDの作成日を取得する方法は次のとおりです。

Date creationDate = objectId.getDate();

同様に、IDのタイムスタンプを秒単位で取得できます。

int timestamp = objectId.getTimestamp();

ObjectId クラスは、カウンター、マシンID、またはプロセスIDを取得するためのメソッドも提供しますが、これらはすべて非推奨です。

4. IDの取得

覚えておくべき主なことは、MongoDBでは、クライアントがDocumentの一意の識別子を生成してからクラスターに送信することです。 これは、リレーショナルデータベースのシーケンスとは対照的です。 これにより、このIDの取得が非常に簡単になります。

4.1. ドライバー生成ID

ドキュメントの一意のIDを生成する標準的で簡単な方法は、ドライバーにジョブを実行させることです。新しいドキュメントコレクションに挿入すると、 _idフィールドがDocumentに存在しない場合、ドライバーは、挿入コマンドをクラスターに送信する前に、新しいObjectIdを生成します。

新しいドキュメントをコレクションに挿入するコードは次のようになります。

Document document = new Document();
document.put("name", "Shubham");
document.put("company", "Baeldung");
collection.insertOne(document);

IDの生成方法を示すことは決してないことがわかります。

insertOne()メソッドが戻ると、Documentから生成されたObjectIdを取得できます。

ObjectId objectId = document.getObjectId("_id");

Documentの標準フィールドのようにObjectIdを取得し、それをObjectIdにキャストすることもできます。

ObjectId oId = (ObjectId) document.get("_id");

4.2. カスタムID

IDを取得する別の方法は、コードでIDを生成し、他のフィールドと同じようにドキュメントに配置することです。 [X130X]Documentを_idとともに送信する場合]フィールドをドライバに追加すると、新しいフィールドは生成されません。

コレクションDocumentを挿入する前に、MongoDB DocumentのIDが必要な場合にこれが必要になることがあります。

クラスの新しいインスタンスを作成することにより、新しいObjectIdを生成できます。

ObjectId generatedId = new ObjectId();

または、 ObjectIdクラスの静的get()メソッドを呼び出すこともできます。

ObjectId generatedId = ObjectId.get();

次に、 Document を作成し、生成されたIDを使用する必要があります。 そのために、Documentコンストラクターで提供できます。

Document document = new Document("_id", generatedId);

または、 put()メソッドを使用することもできます。

document.put("_id", generatedId);

ユーザー生成IDを使用する場合、IDの重複は禁止されているため、挿入するたびに新しいObjectIdを生成するように注意する必要があります。 IDが重複すると、キーメッセージが重複するMongoWriteExceptionが発生します。

ObjectId クラスは、識別子の一部を設定できるようにする他のいくつかのコンストラクターを提供します。

public ObjectId(final Date date)
public ObjectId(final Date date, final int counter)
public ObjectId(final int timestamp, final int counter)
public ObjectId(final String hexString)
public ObjectId(final byte[] bytes)
public ObjectId(final ByteBuffer buffer)

ただし、ドライバーに提供されるIDの一意性はコードに完全に依存しているため、これらのコンストラクターを使用する場合は十分に注意する必要があります。 これらの特定のケースでは、重複キーエラーが発生する可能性があります。

  • 同じ日付(またはタイムスタンプ)とカウンターコンボを複数回使用する場合
  • 同じ16進数のString byte 配列、またはByteBufferを数回使用する場合

5. 結論

この記事では、ドキュメントのMongoDB一意識別子とは何か、そしてそれがどのように機能するかを学びました。 次に、ドキュメントコレクションに挿入した後と、挿入する前の両方で取得する方法を確認しました。

いつものように、これらの例のコードはGitHubから入手できます。