Overview
このガイドでは、 Java Reactive Streams ドライバーでJavaオブジェクトとBSONデータのエンコードとデコードを処理する Codec とサポート クラスについて学習できます。Codec 抽象化を使用すると、任意のJava型を対応するBSON型にマップできます。この抽象化を使用すると、Document や などの中間マップベースのオブジェクトを使用する代わりに、ドメイン オブジェクトをBSONとの間で直接マッピングできます。BsonDocument
次のセクションでは、Codec 抽象化を使用してカスタム エンコードおよびデコード ロジックを指定する方法について説明します。
POJO(Plain Old Java Object)のエンコードおよびデコード ロジックをカスタマイズする方法については、「 POJO を使用したデータのモデル化」ガイドを参照してください。
コーデックの実装
Codec インターフェースには、 JavaオブジェクトをBSONデータにシリアル化および逆シリアル化するための抽象メソッドが含まれています。このインターフェースの実装では、 BSONとJavaオブジェクト間の変換ロジックを定義します。
Codec インターフェースを実装するには、encode()、decode()、および getEncoderClass() 抽象メソッドをオーバーライドします。
encode() メソッドには次のパラメーターが必要です。
Parameter Type | 説明 |
|---|---|
| BSONドキュメント を書込むためのメソッドを公開するインターフェース型である |
| 実装によってエンコードされるデータ。型は、実装に割り当てられた型変数と一致する必要があります。 |
| 現在の値をMongoDBコレクションに保存するかどうかなど、 BSONにエンコードされるJavaオブジェクトデータに関するメタデータが含まれます。 |
このメソッドは、BsonWriter インスタンスを使用してエンコードされた値を MongoDB に送信し、値を返しません。
decode() メソッドは、BSON データから取得した値が設定された Java オブジェクト インスタンスを返します。このメソッドには次のパラメータが必要です。
Parameter Type | 説明 |
|---|---|
| BSON ドキュメントを読み取るためのメソッドを公開するインターフェース型である |
| Java オブジェクトにデコードされる BSON データに関する情報が含まれます。 |
Java は型消去により型を推論できないため、 getEncoderClass() メソッドはJavaクラスのクラスインスタンスを返します。
例
次のコード例は、カスタム Codec を実装する方法を示しています。
ReadStatus列挙には、書籍が読み取られたかどうかを表す値 READ と UNREAD が含まれています。
public enum ReadStatus { READ, UNREAD }
ReadStatusCodecクラスは、 Java enum 値を対応するBSONブール値値に変換するために Codec を実装します。 encode() メソッドは ReadStatus をBSONブール値に変換し、decode() メソッドは逆方向の変換を実行します。
public class ReadStatusCodec implements Codec<ReadStatus> { public void encode(BsonWriter writer, ReadStatus value, EncoderContext encoderContext) { writer.writeBoolean(value.equals(ReadStatus.READ) ? Boolean.TRUE : Boolean.FALSE); } public ReadStatus decode(BsonReader reader, DecoderContext decoderContext) { return reader.readBoolean() ? ReadStatus.READ : ReadStatus.UNREAD; } public Class<ReadStatus> getEncoderClass() { return ReadStatus.class; } }
とそれが適用されるJavaオブジェクトタイプとの間のマッピングを含む のインスタンスをReadStatusCodec CodecRegistryCodecに追加できます。このページの「CodecRegistry」セクションに進み、 Codecを含める方法を確認します。
このセクションのクラスとインターフェースの詳細については、次の API ドキュメントを参照してください。
CodecRegistry の使用
CodecRegistry は、指定された Java クラスをエンコードおよびデコードする Codec インスタンスの不変のコレクションです。次の CodecRegistries クラスの静的ファクトリー メソッドのいずれかを使用して、関連付けられた型に含まれる Codec インスタンスから CodecRegistry を構築できます。
fromCodecs()fromProviders()fromRegistries()
次のコード スニペットは、fromCodecs() メソッドを使用して CodecRegistry を構築する方法を示しています。
CodecRegistry codecRegistry = CodecRegistries.fromCodecs(new IntegerCodec(), new ReadStatusCodec());
前の例では、CodecRegistry には次の Codec の実装が含まれています。
IntegerCodec、Integersを変換し、BSON パッケージの一部となるCodecです。ReadStatusCodec である、
CodecJava列挙値をBSONブール値に変換するサンプル 。
次のコードを使用して、前の例の CodecRegistryインスタンスから Codec インスタンスを検索できます。
Codec<ReadStatus> readStatusCodec = codecRegistry.get(ReadStatus.class); Codec<Integer> integerCodec = codecRegistry.get(Integer.class);
登録されていないクラスの Codecインスタンスを検索しようとすると、get() メソッドは CodecConfigurationException をスローします。
このセクションのクラスとインターフェースの詳細については、次の API ドキュメントを参照してください。
CodecProvider の使用
CodecProvider は、Codec インスタンスを作成し、それを CodecRegistry インスタンスに割り当てる抽象メソッドを含むインターフェースです。CodecRegistry と同様に、BSON ライブラリは get() メソッドによって検索された Codec インスタンスを使用して、Java と BSON データ型間の変換を行います。
ただし、対応する Codec オブジェクトを必要とするフィールドを含むクラスを追加する場合は、クラスの Codec をインスタンス化する前に、クラスフィールドの Codec オブジェクトをインスタンス化する必要があります。 get() メソッドの CodecRegistry パラメータを使用して、Codec が依存する Codec インスタンスを渡すことができます。
次のコード例は、 CodecProvider を実装して、前の例の ReadStatusCodec など、CodecRegistryインスタンスで必要な Codec インスタンスを BookCodec に渡す方法を示しています。
public class BookCodecProvider implements CodecProvider { public BookCodecProvider() {} public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) { if (clazz == Book.class) { return (Codec<T>) new BookCodec(registry); } // return null when not a provider for the requested class return null; } }
これらのCodec クラスを使用して読み取りおよび書込み (write) 操作を示す実行可能な例については、このガイドの「 カスタム コーデックの例 」セクションを参照してください。
POJOPojoCodecProvider を使用する場合は、よく使用されるデータ型を変換し、その動作をカスタマイズするときに、重複コードを最小限に抑えるために の使用を検討してください。詳細については、「 POJO を使用してデータをモデル化する 」ガイドを参照してください。
Default Codec Registry の使用
デフォルトのコーデック レジストリは、一般的に使用されるJavaとMongoDB の型間の変換を指定する CodecProvider クラスのセットです。別のコーデック レジストリを指定しない限り、ドライバーは自動的にデフォルトのコーデック レジストリを使用します。
1 つ以上の Codec クラスの動作をオーバーライドする必要があり、他のクラスのデフォルトのコーデック レジストリの動作を維持する場合は、優先順位に従ってすべてのレジストリを指定できます。例、列挙型の Codec のデフォルトのデフォルト動作をカスタム MyEnumCodec でオーバーライドするには、次の例に示すように、デフォルトのコーデック レジストリの前にそれをレジストリ リストに追加します。
CodecRegistry newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new MyEnumCodec()), MongoClientSettings.getDefaultCodecRegistry());
このセクションのクラスとインターフェースの詳細については、次の API ドキュメントの各セクションを参照してください。
タイプ マッピングをカスタマイズする
BsonTypeClassMapクラスには、 BSONとJava型間の推奨マッピングが含まれています。このクラスをカスタム Codec または CodecProvider で使用すると、Documentクラスなどの Iterable または Map を実装するコンテナクラスにBSON型をデコードするJava型を管理するのに役立ちます。
新しいエントリまたは置換エントリを含む Map を渡すことで、BsonTypeClassMap のデフォルトマッピングを追加または変更できます。
次のコード スニペットは、デフォルトのBsonTypeClassMapインスタンス内のBSON型に対応するJavaクラスの種類を検索する方法を示しています。
BsonTypeClassMap bsonTypeClassMap = new BsonTypeClassMap(); Class<?> clazz = bsonTypeClassMap.get(BsonType.ARRAY); System.out.println("Java type: " + clazz.getName());
このコードは、次のように出力します。
Java type: java.util.List
BsonTypeClassMap コンストラクターで置換を指定することにより、インスタンス内のこれらのマッピングを変更できます。次の例は、BsonTypeClassMapインスタンス内の ARRAY のマッピングを Setクラスに置き換える方法を示しています。
Map<BsonType, Class<?>> replacements = new HashMap<BsonType, Class<?>>(); replacements.put(BsonType.ARRAY, Set.class); BsonTypeClassMap bsonTypeClassMap = new BsonTypeClassMap(replacements); Class<?> clazz = bsonTypeClassMap.get(BsonType.ARRAY); System.out.println("Java type: " + clazz.getName());
このコードは、次のように出力します。
Java type: java.util.Set
デフォルトのマッピングの完全なリストについては、 BsonTypeClassMap APIドキュメントを参照してください。
Tip
クラスが を使用する方法の例については、DocumentBsonTypeClassMap DocumentCodecProvider クラスと DocumentCodec クラスのドライバーソースコードを参照してください。
カスタム Codec の例
このセクションでは、Codec と CodecProvider を実装して、カスタムJavaクラスのエンコードおよびデコード ロジックを定義する方法を示します。また、カスタム実装を指定して使用し、挿入操作と検索操作を実行する方法も示します。
次の例では、Book というカスタムクラスと、 MongoDBコレクションでのストレージと検索用のフィールドを定義します。
public class Book { private String title; private ReadStatus readStatus = ReadStatus.UNREAD; private Integer pageCount; public Book() {} // ...
このクラスには次のフィールドが含まれており、それぞれに Codec が必要です。
titleにはString値が含まれており、この例ではBSONライブラリに含まれるStringCodecが使用されています。readStatusは、書籍が読み取られたかどうかを示します。この例では、列挙値をBSONブール値に変換するカスタム ReadStatusCodec を使用しています。pageCountにはInteger値が含まれており、この例ではBSONライブラリに含まれるIntegerCodecが使用されています。
次のコード例は、 Bookクラスに Codec を実装する方法を示しています。コンストラクターは、フィールドをエンコードおよびデコードするために必要な Codec インスタンスを検索する CodecRegistry のインスタンスを期待していることに注意してください。
public class BookCodec implements Codec<Book> { private Codec<String> stringCodec; private Codec<ReadStatus> readStatusCodec; private Codec<Integer> integerCodec; public BookCodec(CodecRegistry registry) { this.stringCodec = registry.get(String.class); this.readStatusCodec = registry.get(ReadStatus.class); this.integerCodec = registry.get(Integer.class); } // Defines an encode() method to convert Book field values to BSON values public void encode(BsonWriter writer, Book value, EncoderContext encoderContext) { writer.writeStartDocument(); writer.writeName("title"); stringCodec.encode(writer, value.getTitle(), encoderContext); writer.writeName("readStatus"); readStatusCodec.encode(writer, value.getReadStatus(), encoderContext); writer.writeName("pageCount"); integerCodec.encode(writer, value.getPageCount(), encoderContext); writer.writeEndDocument(); } // Defines a decode() method to convert BSON values to Book field values public Book decode(BsonReader reader, DecoderContext decoderContext) { Book book = new Book(); reader.readStartDocument(); while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { String fieldName = reader.readName(); if (fieldName.equals("title")) { book.setTitle(stringCodec.decode(reader, decoderContext)); } else if (fieldName.equals("readStatus")) { book.setReadStatus(readStatusCodec.decode(reader, decoderContext)); } else if (fieldName.equals("pageCount")) { book.setPageCount(integerCodec.decode(reader, decoderContext)); } else if (fieldName.equals("_id")) { reader.readObjectId(); } else { reader.skipValue(); } } reader.readEndDocument(); return book; } // Returns an instance of the Book class, since Java cannot infer the class type public Class<Book> getEncoderClass() { return Book.class; } }
フィールドの Codec インスタンスを Book で使用できるようにするには、次のコード例に示すカスタム CodecProvider を実装します。
public class BookCodecProvider implements CodecProvider { public BookCodecProvider() {} public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) { if (clazz == Book.class) { return (Codec<T>) new BookCodec(registry); } // return null when not a provider for the requested class return null; } }
変換ロジックを定義した後、次の操作を実行できます。
Bookのインスタンスのデータを MongoDB に保存するMongoDB から次のインスタンスにデータを検索する
Book
次の例クラスには、BookCodecProvider を withCodecRegistry() メソッドに渡して MongoCollectionインスタンスに割り当てるコードが含まれています。この例クラスでは、Bookクラスと関連するコーデックを使用してデータの挿入と検索も行います。
public class BookCodecExample { public static void main(String[] args) { String uri = "<MongoDB connection URI>"; try (MongoClient mongoClient = MongoClients.create(uri)) { CodecRegistry codecRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new ReadStatusCodec()), CodecRegistries.fromProviders(new BookCodecProvider()), MongoClientSettings.getDefaultCodecRegistry()); MongoDatabase database = mongoClient.getDatabase("codecs_example_db"); MongoCollection<Book> collection = database.getCollection("books", Book.class) .withCodecRegistry(codecRegistry); // construct and insert an instance of Book Book myBook = new Book(); myBook.setTitle("The Hobbit"); myBook.setReadStatus(ReadStatus.READ); myBook.setPageCount(310); Mono.from(collection.insertOne(myBook)).block(); // retrieve one or more instances of Book Flux.from(collection.find()) .doOnNext(System.out::println) .blockLast(); } } }
上記の例を実行すると、出力は次のようになります。
Book [title=The Hobbit, readStatus=READ, pageCount=310]
API ドキュメント
このガイドで言及されているメソッドとクラスの詳細については、次のAPIドキュメントを参照してください。