POJO カスタマイズ
項目一覧
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
には、次のフィールドが含まれます。
フィールド名 | 説明 |
---|---|
名前 | ClassModel に関連付ける POJO クラス名。 |
InstanceCreatorFactory | POJO の新しいインスタンスを作成する新しいインスタンス ファクトリを含みます。デフォルトでは、POJO に空のコンストラクターが必要です。 |
PropertyModels | POJO のフィールドを BSON との間でデータ変換する方法を指定する PropertyModel インスタンスのリストが含まれています。 |
IdPropertyModelHolder | ドキュメントの _id フィールドに対応する POJO フィールドを指定します。任意。 |
弁別子キー | 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 | フィールドのデータ型を記述する org.bson.codecs.pojo.TypeData のインスタンスを含みます。 |
コーデック | フィールドのエンコードまたはデコードに使用するコーデックを指定します。任意。 |
直列化チェッカー | チェッカーで指定された基準を使用して値を直列化するかどうかを決定します。 |
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
クラスの次の静的フィールドからアクセスできます。
フィールド名 | 説明 |
---|---|
ANNOTATION_CONVENTION | お使いの POJO 向け org.bson.codecs.pojo.annotations パッケージで定義済みの注釈を有効にします。詳細については、注釈のセクションを参照してください。 |
CLASS_AND_PROPERTY_CONVENTION | 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 . |
DEFAULT_CONVENTIONS | Enables the following Conventions: - CLASS_AND_PROPERTY_CONVENTION - ANNOTATION_CONVENTION - OBJECT_ID_GENERATORS |
NO_CONVENTIONS | 空のリストを提供します。 |
OBJECT_ID_GENERATORS | id プロパティで ObjectId 値を使用する各 ClassModel に新しい ObjectId を追加する既定の IdGenerator を追加します。 |
SET_PRIVATE_FIELDS_CONVENTION | setter メソッドを必要とせずにリフレクションを使用してプライベートフィールドを設定するために、 ClassModel を有効にします。 |
USE_GETTERS_FOR_SETTERS | setter メソッドが存在しない場合、 Collection フィールドと Map フィールドのセッターとして getter メソッドの使用を有効にします。 |
次のいずれかの方法を指定して Conventions を指定できます。
カスタムの Convention を作成するには、Convention
インターフェースを実装するクラスを作成し、ClassModelBuilder
インスタンスにアクセスできる apply()
メソッドを上書きします。
注釈
POJO クラスの getter メソッドと setter メソッドに注釈を適用できます。これらの注釈により、特定のフィールド、メソッド、またはクラスの ClassModel
と PropertyModel
の動作を構成します。
次の注釈は、org.bson.codecs.pojo.annotations パッケージから使用できます。
注釈名 | 説明 |
---|---|
BsonCreator | 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 BsonProperty or BsonId annotations. |
BsonDiscriminator | クラスが弁別子を使用することを指定します。カスタム弁別子のキーと値を設定できます。 |
BsonRepresentation | POJOプロパティと異なる場合に、値の保存に使用されるBSONタイプを指定します。 このページの BsonRepresentation エラーの例 の例を参照してください。 |
BsonId | シリアル化するプロパティを _id プロパティとしてマークします。 |
BsonIgnore | 無視するプロパティをマークします。プロパティをシリアル化するか、逆シリアル化するかを構成できます。 |
BsonProperty | 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. |
BsonExtraElements | 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 an example of a BsonExtraElements Annotation Example. |
次のコード スニペットは、前述の注釈をいくつか使用した 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
の設定例については、 ANSI の例を参照してください。 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
型のコーデックを必ず登録してください。
含まれているコーデックの登録方法の詳細については、「デフォルトのコーデック レジストリ」に関するドキュメントを参照してください。