Overview
このガイドでは、MongoDB Java ドライバーにおける BSON と POJO の間のカスタムデータ変換を定義する方法を学ぶことができます。 POJOに関するガイドでは、1 つ以上の POJO クラスとそのプロパティに対するデータ変換の方法を指示するクラスを含む PojoCodecProviderを指定する方法を示します。
ClassModelクラスとPropertyModelクラスを使用してデータ変換を指定する方法を示します。 より具体的なカスタマイズについては、「高度な構成 」のセクションを参照してください。
規則や注釈などのヘルパーを使用してよく行われる直列化アクションの指定方法も紹介します。
同じコレクション内のドキュメントで複数の POJO クラスを直列化する場合は、「識別子」セクションを参照してください。
条件付きシリアル化を実装する必要がある場合、または列挙型、ジェネリック、インターフェース型、または抽象型を使用する必要がある場合は、「高度な構成 」のセクションを参照してください。
事前定義された動作のみを使用して BSON と POJO 間でデータを変換する場合は、『ドキュメント データ形式: POJO』ガイドに示されている PojoCodecProvider の自動設定を使用できます。
PojoCodecProvider のカスタマイズ
このセクションでは、データ変換ロジックと POJO クラスを PojoCodecProvider で指定する方法について説明します。PojoCodecProvider は、データ変換で使用するコーデックを指定する CodecProvider インターフェイスの実装です。BSON と POJO の間でデータ変換を行う場合は、この実装を使用します。
PojoCodecProvider.builder() メソッドを使用して PojoCodecProvider インスタンスを作成できます。ビルダにメソッドを連鎖させて、次のいずれかを登録することもできます。
個々の POJO クラス
POJO クラスを含むパッケージ名
特定の POJO クラスの変換ロジックを記述する
ClassModelのインスタンス
次の例は、「org.example.pojos」という名前のパッケージで POJO を指定する方法と、PojoCodecProvider を CodecRegistry に追加する方法を示しています。
import org.bson.codecs.configuration.CodecProvider; import org.bson.codecs.configuration.CodecRegistry; import org.bson.codecs.pojo.PojoCodecProvider; import static org.bson.codecs.configuration.CodecRegistries.fromRegistries; import static org.bson.codecs.configuration.CodecRegistries.fromProviders; import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry; CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register("org.example.pojos").build(); CodecRegistry pojoCodecRegistry = fromRegistries(getDefaultCodecRegistry(), fromProviders(pojoCodecProvider)); // Call withCodecRegistry(pojoCodecRegistry) on an instance of MongoClient, MongoDatabase, or MongoCollection
このクラスの詳細については、PojoCodecProvider.Builder APIドキュメント を参照してください。
ClassModel
ClassModel インスタンスには、特定の POJO クラスに関するデータ変換情報が格納されます。これには、POJO のプロパティ フィールド、フィールドを変換するかどうか、および任意でフィールドを変換するための Codecs を記述する PropertyModel インスタンスのリストが含まれています。
ClassModel には、次のフィールドが含まれます。
フィールド名 | 説明 |
|---|---|
名前 |
|
InstanceCreatorFactory | POJO の新しいインスタンスを作成する新しいインスタンス ファクトリを含みます。デフォルトでは、POJO に空のコンストラクターが必要です。 |
PropertyModels | POJO のフィールドを BSON との間でデータ変換する方法を指定する |
IdPropertyModelHolder | ドキュメントの |
弁別子キー | Specifies the name of the discriminator field. Optional. For more information about discriminators, see the Discriminators section. |
弁別子の値 | Specifies the lookup value that represents the POJO class. Optional. For more information about discriminators, see the Discriminators section. |
弁別子フラグ | 弁別子をシリアル化するかどうかを指定します。デフォルトではオフです。任意。 |
このクラスの詳細については、「ClassModel API ドキュメント」を参照してください。
ClassModel をインスタンス化するには、ClassModel.builder() メソッドを使用して POJO クラスを指定します。ビルダはリフレクションを使用して必要なメタデータを作成します。
ClassModel<Flower> classModel = ClassModel.builder(Flower.class).build();
PropertyModel
PropertyModel には、ドキュメント内の特定のフィールドを直列化および逆直列化する方法に関する情報が格納されます。
この PropertyModel には、次の情報が含まれています。
フィールド名 | 説明 |
|---|---|
名前 | モデル内のプロパティ名を指定します。 |
Read Name | BSON に直列化する時にキーとして使用するプロパティの名前。 |
Write Name | BSON から逆直列化する時にキーとして使用するプロパティの名前。 |
Type data | フィールドのデータ型を記述する |
コーデック | フィールドのエンコードまたはデコードに使用するコーデックを指定します。任意。 |
直列化チェッカー | チェッカーで指定された基準を使用して値を直列化するかどうかを決定します。 |
Property accessor | POJO からプロパティの値にアクセスするために使用されるメソッド。 |
useDiscriminator | Specifies whether to use the discriminator. For more information about discriminators, see the Discriminators section. |
PropertyModel を作成するには、PropertyModel.builder() メソッドを呼び出してインスタンス化できる PropertyModelBuilder を使用します。
このクラスの詳細については、PropertyModel.Builder APIドキュメントを参照してください。
Conventions
Convention インターフェースには、ClassModel または PropertyModel の動作を変更する構成オプションが含まれています。PojoCodecProvider.Builder.conventions() または ClassModelBuilder.conventions() の呼び出しで Convention を指定できます。
注意
ビルダは Convention インスタンスを順番に適用します。これにより、前に適用されたインスタンスで定義された動作が上書きされます。
BSON ライブラリで定義された Convention インスタンスには、Conventions クラスの次の静的フィールドからアクセスできます。
フィールド名 | 説明 |
|---|---|
| お使いの POJO 向け |
| Sets the following default values for the ClassModel and PropertyModel instances:- Discriminator key to _t- Discriminator value to the ClassModel simple type name- Id field to _id for each PropertyModel. |
| Enables the following Conventions: - CLASS_AND_PROPERTY_CONVENTION- ANNOTATION_CONVENTION- OBJECT_ID_GENERATORS |
| 空のリストを提供します。 |
|
|
| setter メソッドを必要とせずにリフレクションを使用してプライベートフィールドを設定するために、 |
| setter メソッドが存在しない場合、 |
次のいずれかの方法を指定して Conventions を指定できます。
カスタムの Convention を作成するには、Convention インターフェースを実装するクラスを作成し、ClassModelBuilder インスタンスにアクセスできる apply() メソッドを上書きします。
注釈
POJO クラスの getter メソッドと setter メソッドに注釈を適用できます。これらの注釈により、特定のフィールド、メソッド、またはクラスの ClassModel と PropertyModel の動作を構成します。
次の注釈は、org.bson.codecs.pojo.annotations パッケージから使用できます。
注釈名 | 説明 |
|---|---|
| Marks a public constructor or a public static method as the creator for new instances of the class. You must annotate all parameters in the constructor with either the |
| クラスが弁別子を使用することを指定します。カスタム弁別子のキーと値を設定できます。 |
| POJOプロパティと異なる場合に、値の保存に使用されるBSONタイプを指定します。このページの 「BsonRepresentation エラーの例」 を参照してください。 |
| シリアル化するプロパティを _id プロパティとしてマークします。 |
| 無視するプロパティをマークします。プロパティをシリアル化するか、逆シリアル化するかを構成できます。 |
| Specifies a custom document field name when converting the POJO
field to BSON. You can include a discriminator to serialize POJOs
nested within the field. When applying @BsonProperty to a private field,
you must also add getter and setter methods for that field to serialize and
customize the field name. |
| Specifies the POJO field on which to deserialize all elements that are
not mapped to a field. The POJO field must be one of the following
types: See the BsonExtraElements Annotation Example on this page. |
次のコード スニペットは、前述の注釈をいくつか使用した Product というサンプル POJO を示しています。
import org.bson.BsonType; import org.bson.codecs.pojo.annotations.BsonCreator; import org.bson.codecs.pojo.annotations.BsonDiscriminator; import org.bson.codecs.pojo.annotations.BsonId; import org.bson.codecs.pojo.annotations.BsonIgnore; import org.bson.codecs.pojo.annotations.BsonProperty; import org.bson.codecs.pojo.annotations.BsonRepresentation; public class Product { private String name; private String serialNumber; private List<Product> relatedItems; public Product( String name) { this.name = name; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } // ... }
Tip
注釈を使用するときは、ClassModelBuilder または PojoCodecProvider.Builder に Conventions.ANNOTATION_CONVENTION を忘れずに指定してください。例:
ClassModel<Product> classModel = ClassModel.builder(Product.class). conventions(Arrays.asList(Conventions.ANNOTATION_CONVENTION)).build();
サンプル POJO の注釈は、次の動作を指定します。
指定された弁別子のキーと値を持つ POJO を参照し、書込み (write) 操作時に "AnnotatedProduct" の値を持つ
clsフィールドを BSON ドキュメントに追加ドキュメント内の POJO の
nameフィールドと値と BSON のmodelNameフィールドと値の間で変換ドキュメント内の POJO の
serialNumberフィールドと値と BSON ドキュメントの_idフィールドと値の間で変換データを変換するときに
relatedItemsフィールドと値を省略POJO をインスタンス化するときに
Product(String name)コンストラクターを使用
BsonExtraElements の例
@BsonExtraElements 注釈を使用すると、対応する POJO フィールド マッピングがない MongoDB ドキュメントからデータを逆直列化するフィールドを指定できます。これは、アプリケーションが部分的に定義されたスキーマのデータを処理する必要がある場合に役立ちます。この注釈を使用すると、POJO のフィールドに対応していないフィールドのデータにアクセスできます。
前例の Product POJO を使用して、オンライン ストアのデータを保存および取得するシナリオを考えてみましょう。店舗に多彩な商品を提供していくと、それら商品を説明する追加フィールドが必要になることに気づきます。追加の各フィールドを POJO にマッピングする代わりに、次のコードで例示するとおり、@BsonExtraElements で注釈が付けられた単一フィールドからそれらにアクセスできます。
public class Product { private String name; private String serialNumber; private List<Product> relatedItems; private Document additionalInfo; // ...
誰かが製品データに dimensions フィールドと weight フィールドを追加して、ドキュメントに次の情報が含まれたとします。
{ "name": "MDB0123", "serialNumber": "62e2...", "dimensions": "3x4x5", "weight": "256g" }
Product POJO を使用して検索された上記のドキュメントには、次のデータが含まれています。
ProductWithBsonExtraElements [ name=MDB0123, serialNumber=62eb..., relatedItems=null, additionalInfo=Document{{dimensions=3x4x5, weight=256g}} ]
BsonRepresentation エラーの例
@BsonRepresentationアノテーションを使用すると、POJO クラスのフィールドを別のデータ型として MongoDB database に保存できます。 このページの 注釈 セクションの Product POJO コードの例では、@BsonRepresentation Stringを使用して 値をObjectId 値としてデータベース ドキュメントに保存します。
ただし、@BsonRepresentation アノテーションを使用して String と ObjectId 以外のデータ型を変換すると、次のエラーメッセージが表示されます。
Codec must implement RepresentationConfigurable to support BsonRepresentation
たとえば、次のコードは、purchaseDate タイプのLong フィールドをProduct POJO に追加します。この例では、 @BsonRepresentationを使用して、 Longの値を データベース内のDateTime値として表現しようとしています。
public class Product { private String name; private String serialNumber; private Long purchaseDate; // ... }
上記のコードは エラーになります。 代わりに、タイプLongのpurchaseDate値をDateTimeに変換するカスタム コーデックを作成できます。
public class LongRepresentableCodec implements Codec<Long>, RepresentationConfigurable<Long> { private final BsonType representation; /** * Constructs a LongRepresentableCodec with a Int64 representation. */ public LongRepresentableCodec() { representation = BsonType.INT64; } private LongRepresentableCodec(final BsonType representation) { this.representation = representation; } public BsonType getRepresentation() { return representation; } public Codec<Long> withRepresentation(final BsonType representation) { if (representation != BsonType.INT64 && representation != BsonType.DATE_TIME) { throw new CodecConfigurationException(representation + " is not a supported representation for LongRepresentableCodec"); } return new LongRepresentableCodec(representation); } public void encode(final BsonWriter writer, final Long value, final EncoderContext encoderContext) { switch (representation) { case INT64: writer.writeInt64(value); break; case DATE_TIME: writer.writeDateTime(value); break; default: throw new BsonInvalidOperationException("Cannot encode a Long to a " + representation); } } public Long decode(final BsonReader reader, final DecoderContext decoderContext) { switch (representation) { case INT64: return reader.readInt64(); case DATE_TIME: return reader.readDateTime(); default: throw new CodecConfigurationException("Cannot decode " + representation + " to a Long"); } } public Class<Long> getEncoderClass() { return Long.class; } }
次に、コーデックとそれが適用されるJavaオブジェクトタイプとの間のマッピングを含む LongRepresentableCodec のインスタンスを CodecRegistry に追加します。を使用してカスタム コーデックを登録する手順については、「 タイプ コーデックでデータをエンコードするCodecRegistry 」のガイドを参照してください。
弁別子
弁別子は、特定のドキュメント スキーマを識別するプロパティです。弁別子キーは、スキーマを識別するために使用するドキュメント フィールドを識別します。弁別子の値は、ドキュメント フィールドのデフォルト値を識別します。
弁別子を使用して、同じコレクションから異なるオブジェクト クラスに逆シリアル化するときに使用するオブジェクト クラスを CodecProvider に指示します。POJO を MongoDB コレクションにシリアル化する場合、POJO プロパティ データで特に指定されていない限り、関連付けられたコーデックによって弁別子のキー値フィールドが設定されます。
次のいずれかを実行することにより、POJO で弁別子を設定して有効にすることができます。
POJO クラスの弁別子を指定するには、
@BsonDiscriminatorアノテーションを使用します。POJO クラスに関連付けられた
ClassModelBuilderでenableDiscriminator(true)を呼び出す
@BsonDiscriminator アノテーションを含む次の POJO クラスの例と、弁別子フィールドを含むドキュメントの例を参照してください。
public class AnonymousUser { // class code } public class RegisteredUser { // class code }
以下は、1 つの MongoDB コレクション内の前述の POJO から作成されたサンプル ドキュメントを示しています。
{ "_cls": "AnonymousUser", "_id": ObjectId("<Object ID>"), ... } { "_cls": "RegisteredUser", "_id": ObjectId("<Object ID>"), ... }
高度な構成
プロパティの抽象型またはインターフェイス型
抽象クラスまたはインターフェース型のプロパティを含む POJO をシリアル化するには、型とそのすべてのサブタイプまたは実装に対して弁別子を指定する必要があります。
次のように、フィールドの 1 つで抽象クラス User を参照する POJO を定義したとします。
public class UserRecordPojo { private User user; // ... }
User 抽象クラスにサブクラス FreeUser と SubscriberUser がある場合は、次のように POJO クラスと抽象クラスを CodecRegistry に追加できます。
ClassModel<UserRecordPojo> userRecordPojo = ClassModel.builder(UserRecordPojo.class).enableDiscriminator(true).build(); ClassModel<User> userModel = ClassModel.builder(User.class).enableDiscriminator(true).build(); ClassModel<FreeUser> freeUserModel = ClassModel.builder(FreeUser.class).enableDiscriminator(true).build(); ClassModel<SubscriberUser> subscriberUserModel = ClassModel.builder(SubscriberUser.class).enableDiscriminator(true).build(); PojoCodecProvider pojoCodecProvider = PojoCodecProvider.builder().register(userRecordPojo, userModel, freeUserModel, subscriberUserModel).build(); CodecRegistry pojoCodecRegistry = fromRegistries(getDefaultCodecRegistry(), fromProviders(pojoCodecProvider));
弁別子の指定の詳細については、このガイドの「弁別子」に関するセクションを参照してください。
引数なしのコンストラクターのない POJO
POJO Codecs はデフォルトで、引数のない空のコンストラクターを呼び出します。別のコンストラクターを指定するには、POJO で次の操作を実行する必要があります。
ANNOTATION_CONVENTION設定をClassModelBuilderに渡すBsonCreatorアノテーションを使ってコンストラクタを特定する
ANNOTATION_CONVENTION の設定例については、annotation_CONNECTION のヒント を参照してください。BsonCreator アノテーションの例については、サンプルPOJO コードを参照してください。
直列化のカスタマイズ
デフォルトでは、ClassModelBuilder は POJO 内の null 以外のすべてのプロパティのシリアル化を試みます。プロパティ値が null の場合、デフォルトの PropertySerialization 実装はそのフィールドをスキップします。
POJO シリアル化の動作をカスタマイズするには、次のいずれかを実行します。
常に直列化をスキップするには、プロパティに
@BsonIgnoreアノテーションを使用します。 適切な規則 を使用して注釈を有効にしてください。PropertySerializationインターフェースのshouldSerialize()メソッドを上書きするカスタム クラスを作成します。ClassModelBuilderからアクセスできるPropertyModelBuilderにカスタム実装を指定します。
POJO で@BsonIgnoreアノテーションを使用する方法の詳細については、このガイドの注釈 に関するセクションを参照してください。
次のサンプル コードは、フィールドをシリアル化するかどうかを判断するためのデフォルトの条件を上書きするために PropertySerialization インターフェースを実装するカスタム クラスを示しています。
public class CourteousAgeSerialization implements PropertySerialization<Integer> { public boolean shouldSerialize(Integer value) { return (value < 30); } }
前のクラスでは、「29 より大きい整数はシリアル化されず、したがって MongoDB ドキュメントに含まれない」ことを指定しています。このカスタム シリアル化動作を次のサンプル POJO に適用したとします。
public class BirthdayInvitation { private String name; private Integer age; private LocalDateTime eventDateTime; // ... }
カスタム シリアル化を指定するには、次のコードを使用して age フィールドに関連付けられた ClassModel プロパティから CourteousAgeSerialization インスタンスを PropertyModelBuilder に追加します。
ClassModelBuilder<BirthdayInvitation> classModel = ClassModel.builder(BirthdayInvitation.class); ((PropertyModelBuilder<Integer>) classModel.getProperty("age")) .propertySerialization(new CourteousAgeSerialization()); PojoCodecProvider pojoCodecProvider = PojoCodecProvider.builder().register(classModel.build()).build(); CodecRegistry pojoCodecRegistry = fromRegistries(getDefaultCodecRegistry(), fromProviders(pojoCodecProvider));
age フィールドに 29 より大きい値を含む POJO を挿入すると、直列化されたドキュメントではその値が省略されます。POJO 宣言と結果のドキュメントは、次のようになります。
// constructor with parameters for name, age, and eventDateTime, respectively BirthdayInvitation invitation = new BirthdayInvitation( "Galadriel", 7582, LocalDateTime.of(2021, Month.JANUARY, 18, 30, 0) );
age フィールドの値が 29 より大きいため、直列化されたドキュメントは次のようになります。
{ "_id" : ObjectId("..."), "eventDateTime" : ..., "name" : "Galadriel" }
ジェネリック サポート
以下の条件を満たす場合、POJO Codec を使用してジェネリック プロパティを含むクラスをシリアル化できます。
境界のある具象型パラメーターのみを含む
POJO または そのフィールドのいずれかがクラス階層の一部である場合、最上位の POJO は型パラメータを含まない
ClassModelBuilder は、型消去を回避するために具体的な型パラメーターを検査して保存します。JVM は型パラメーター情報を削除するため、具象型パラメーターのないジェネリック プロパティを含むクラスをシリアル化することはできません。
型パラメータを保存するには、PropertyCodecProvider インタフェースを実装して POJO で定義されたジェネリック型に指定できます。次のコード スニペットは、Guava Optional クラスにシリアル化の互換性を追加する PropertyCodecProvider の実装例を示しています。
Optional フィールドを持つ次の POJO をシリアル化したいとします。
public class ApplicationUser { private Optional<Address> optionalAddress; private Optional<Subscription> optionalSubscription; // ... }
カスタム コーデックを取得するには、次の PropertyCodecProvider の実装を使用できます。この実装では、TypeWithTypeParameters インターフェースを使用して型情報にアクセスします。
public class OptionalPropertyCodecProvider implements PropertyCodecProvider { public <T> Codec<T> get(final TypeWithTypeParameters<T> type, final PropertyCodecRegistry registry) { // Check the main type and number of generic parameters if (Optional.class.isAssignableFrom(type.getType()) && type.getTypeParameters().size() == 1) { // Get the codec for the concrete type of the Optional, as its declared in the POJO. Codec<?> valueCodec = registry.get(type.getTypeParameters().get(0)); return new OptionalCodec(type.getType(), valueCodec); } else { return null; } } private static final class OptionalCodec<T> implements Codec<Optional<T>> { private final Class<Optional<T>> encoderClass; private final Codec<T> codec; private OptionalCodec(final Class<Optional<T>> encoderClass, final Codec<T> codec) { this.encoderClass = encoderClass; this.codec = codec; } public void encode(final BsonWriter writer, final Optional<T> optionalValue, final EncoderContext encoderContext) { if (optionalValue != null && optionalValue.isPresent()) { codec.encode(writer, optionalValue.get(), encoderContext); } else { writer.writeNull(); } } public Optional<T> decode(final BsonReader reader, final DecoderContext context) { return Optional.of(codec.decode(reader, context)); } public Class<Optional<T>> getEncoderClass() { return encoderClass; } } }
次のように、OptionalPropertyCodecProvider を PojoCodecProvider と POJO を含むパッケージに登録します。
CodecProvider pojoCodecProvider = PojoCodecProvider.builder() .register("org.example.pojos") .register(new OptionalPropertyCodecProvider()) .build();
このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。
ジェネリックと型パラメータの詳細については、「 ジェネリック型の呼び出しとインスタンス化に関するJava言語ガイド 」を参照してください。
列挙型のサポート
ドライバー バージョン 4.5 以降では、PojoCodecProvider に enum 型を変換するコーデックが含まれなくなりました。デフォルトのコーデック レジストリにあるコーデックなど、必要な場合は enum 型のコーデックを必ず登録してください。
含まれているコーデックの登録方法の詳細については、「デフォルトのコーデック レジストリ」に関するドキュメントを参照してください。
よくある質問
このセクションでは、データ変換ロジックを定義するときに発生し得る質問に答えます。
の直列化を制御できますか。LocalDate
はい、 Javaドライバー v3.7が、JSR-310 Instant、LocalDate、LocalDateTime のネイティブ サポートを追加します。
を yyy-mm-d形式の string としてシリアル化できますか。java.util.Date
はい、このクラス用に独自のコーデックをビルドし、それをレジストリに追加できます。
コーデックをプロバイダーのリストの最初の、デフォルトのコーデック レジストリの前、およびPojoCodecProviderの前に追加します。
CodecRegistry registry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs( new MyDateAsStringCodec()), MongoClientSettings.getDefaultCodecRegistry(), fromProviders(pojoCodecProvider));
ゲッターまたはセッターを使用せずに、POJO を フィールドに直接読み取り/書込み (read)/書込み (write) することはできますか。
パブリック セットが使用できない場合に、リフレクションを通じてプライベート フィールドを設定するSET_PRIVATE_FIELDS_CONVENTIONを使用するようにPojoCodecProviderを構成できます。
特定の POJO クラスのコレクション名を指定するにはどうすればよいですか? 注釈はありますか。
注釈はありません。次のコードに示すように、クラスに静的文字列を追加することをお勧めします。
public class Person { public static final String COLLECTION_NAME = "people"; }
次のスニペットは、特定の POJO クラスのコレクション名を指定します。
database.getCollection(Person.COLLECTION_NAME, Person.class);