Docs Menu
Docs Home
/ / /
Java Sync Driver
/ /

POJO カスタマイズ

項目一覧

  • Overview
  • PojoCodecProvider のカスタマイズ
  • ClassModel
  • PropertyModel
  • Conventions
  • 注釈
  • BsonExtraElements の例
  • BsonRepresentation エラーの例
  • 弁別子
  • 高度な構成
  • プロパティの抽象型またはインターフェイス型
  • 引数なしのコンストラクターのない POJO
  • 直列化のカスタマイズ

このガイドでは、MongoDB Java ドライバーにおける BSON と POJO の間のカスタムデータ変換を定義する方法を学ぶことができます。 POJOに関するガイドでは、1 つ以上の POJO クラスとそのプロパティに対するデータ変換の方法を指示するクラスを含む PojoCodecProviderを指定する方法を示します。

ClassModelクラスとPropertyModelクラスを使用してデータ変換を指定する方法を示します。 より具体的なカスタマイズについては、「高度な構成 」のセクションを参照してください。

規則注釈などのヘルパーを使用してよく行われる直列化アクションの指定方法も紹介します。

同じコレクション内のドキュメントで複数の POJO クラスを直列化する場合は、「識別子」セクションを参照してください。

条件付きシリアル化を実装する必要がある場合、または列挙型、ジェネリック、インターフェース型、または抽象型を使用する必要がある場合は、「高度な構成 」のセクションを参照してください。

事前定義された動作のみを使用して BSON と POJO 間でデータを変換する場合は、『ドキュメント データ形式: POJO』ガイドに示されている PojoCodecProvider自動設定を使用できます。

このセクションでは、データ変換ロジックと POJO クラスを PojoCodecProvider で指定する方法について説明します。PojoCodecProvider は、データ変換で使用するコーデックを指定する CodecProvider インターフェイスの実装です。BSON と POJO の間でデータ変換を行う場合は、この実装を使用します。

PojoCodecProvider.builder() メソッドを使用して PojoCodecProvider インスタンスを作成できます。ビルダにメソッドを連鎖させて、次のいずれかを登録することもできます。

  • 個々の POJO クラス

  • POJO クラスを含むパッケージ名

  • 特定の POJO クラスの変換ロジックを記述する ClassModel のインスタンス

次の例は、「org.example.pojos」という名前のパッケージで POJO を指定する方法と、PojoCodecProviderCodecRegistry に追加する方法を示しています。

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 インスタンスには、特定の 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 には、次の情報が含まれています。

フィールド名
説明
名前
モデル内のプロパティ名を指定します。
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 ドキュメント。

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 メソッドに注釈を適用できます。これらの注釈により、特定のフィールド、メソッド、またはクラスの ClassModelPropertyModel の動作を構成します。

次の注釈は、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 型を指定します。

Tip

以下も参照してください。

BsonId
シリアル化するプロパティを _id プロパティとしてマークします。
BsonIgnore
無視するプロパティをマークします。プロパティをシリアル化するか、逆シリアル化するかを構成できます。
BsonProperty

POJO フィールドを BSON に変換する時のカスタム ドキュメント フィールド名を指定します。フィールド内にネストされたPOJO を直列化するために、弁別子を含めることができます。

重要

@BsonProperty をプライベート フィールドに適用する場合は、そのフィールドの getter メソッドと setter メソッドも追加して、フィールド名をシリアル化およびカスタマイズする必要があります。

BsonExtraElements

フィールドにマップされていないすべての要素を逆直列化する POJO フィールドを指定します。POJO フィールドは次のいずれかのタイプでなければなりません。

Tip

以下も参照してください。

次のコード スニペットは、前述の注釈をいくつか使用した 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;
@BsonDiscriminator(value="AnnotatedProduct", key="_cls")
public class Product {
@BsonProperty("modelName")
private String name;
@BsonId()
@BsonRepresentation(BsonType.OBJECT_ID)
private String serialNumber;
@BsonIgnore
private List<Product> relatedItems;
@BsonCreator
public Product(@BsonProperty("modelName") String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
// ...
}

Tip

注釈を使用するときは、ClassModelBuilder または PojoCodecProvider.BuilderConventions.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 注釈を使用すると、対応する POJO フィールド マッピングがない MongoDB ドキュメントからデータを逆直列化するフィールドを指定できます。これは、アプリケーションが部分的に定義されたスキーマのデータを処理する必要がある場合に役立ちます。この注釈を使用すると、POJO のフィールドに対応していないフィールドのデータにアクセスできます。

前例の Product POJO を使用して、オンライン ストアのデータを保存および取得するシナリオを考えてみましょう。店舗に多彩な商品を提供していくと、それら商品を説明する追加フィールドが必要になることに気づきます。追加の各フィールドを POJO にマッピングする代わりに、次のコードで例示するとおり、@BsonExtraElements で注釈が付けられた単一フィールドからそれらにアクセスできます。

public class Product {
@BsonProperty("modelName")
private String name;
@BsonId()
@BsonRepresentation(BsonType.OBJECT_ID)
private String serialNumber;
@BsonIgnore
private List<Product> relatedItems;
@BsonExtraElements
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アノテーションを使用すると、POJO クラスのフィールドを別のデータ型として MongoDB database に保存できます。 このページの 注釈 セクションの Product POJO コードの例では、@BsonRepresentation Stringを使用して 値をObjectId 値としてデータベース ドキュメントに保存します。

ただし、@BsonRepresentation アノテーションを使用して StringObjectId 以外のデータ型を変換すると、次のエラーメッセージが表示されます。

Codec must implement RepresentationConfigurable to support BsonRepresentation

たとえば、次のコードは、purchaseDate タイプのLong フィールドをProduct POJO に追加します。この例では、 @BsonRepresentationを使用して、 Longの値を データベース内のDateTime値として表現しようとしています。

public class Product {
@BsonProperty("modelName")
private String name;
@BsonId()
@BsonRepresentation(BsonType.OBJECT_ID)
private String serialNumber;
@BsonRepresentation(BsonType.DATE_TIME)
private Long purchaseDate;
// ...
}

上記のコードは エラーになります。 代わりに、タイプLongpurchaseDate値を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;
}
@Override
public BsonType getRepresentation() {
return representation;
}
@Override
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);
}
@Override
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);
}
}
@Override
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");
}
}
@Override
public Class<Long> getEncoderClass() {
return Long.class;
}
}

次に、コーデックとそれが適用される Java オブジェクトタイプとの間のマッピングを含む LongRepresentableCodec のインスタンスを CodecRegistry に追加します。CodecRegistry を使用してカスタム コーデックを登録する手順については、「コーデックガイド」を参照してください。

弁別子は、特定のドキュメント スキーマを識別するプロパティです。弁別子キーは、スキーマを識別するために使用するドキュメント フィールドを識別します。弁別子の値は、ドキュメント フィールドのデフォルト値を識別します。

弁別子を使用して、同じコレクションから異なるオブジェクト クラスに逆シリアル化するときに使用するオブジェクト クラスを CodecProvider に指示します。POJO を MongoDB コレクションにシリアル化する場合、POJO プロパティ データで特に指定されていない限り、関連付けられたコーデックによって弁別子のキー値フィールドが設定されます。

次のいずれかを実行することにより、POJO で弁別子を設定して有効にすることができます。

  • POJO クラスの弁別子を指定するには、@BsonDiscriminator アノテーションを使用します。

  • POJO クラスに関連付けられた ClassModelBuilderenableDiscriminator(true) を呼び出す

@BsonDiscriminator アノテーションを含む次の POJO クラスの例と、弁別子フィールドを含むドキュメントの例を参照してください。

@BsonDiscriminator(value="AnonymousUser", key="_cls")
public class AnonymousUser {
// class code
}
@BsonDiscriminator(value="RegisteredUser", key="_cls")
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 抽象クラスにサブクラス FreeUserSubscriberUser がある場合は、次のように 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 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> {
@Override
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 {
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
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;
}
@Override
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();
}
}
@Override
public Optional<T> decode(final BsonReader reader, final DecoderContext context) {
return Optional.of(codec.decode(reader, context));
}
@Override
public Class<Optional<T>> getEncoderClass() {
return encoderClass;
}
}
}

次のように、OptionalPropertyCodecProviderPojoCodecProvider と POJO を含むパッケージに登録します。

CodecProvider pojoCodecProvider = PojoCodecProvider.builder()
.register("org.example.pojos")
.register(new OptionalPropertyCodecProvider())
.build();

このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。

ジェネリックと型パラメータの詳細については、「ジェネリック型の呼び出しとインスタンス化に関する Java 言語ガイド」を参照してください。

ドライバー バージョン 4.5 以降では、PojoCodecProviderenum 型を変換するコーデックが含まれなくなりました。デフォルトのコーデック レジストリにあるコーデックなど、必要な場合は enum 型のコーデックを必ず登録してください。

含まれているコーデックの登録方法の詳細については、「デフォルトのコーデック レジストリ」に関するドキュメントを参照してください。

戻る

ドキュメントのデータ形式: レコード