Menu Docs
Página inicial do Docs
/ / /
Driver de sincronização Java
/ /

Personalização POJO

Nesta página

  • Visão geral
  • Personalizar um PojoCodecProvider
  • ClassModel
  • PropertyModel
  • Convenções
  • Anotações
  • Exemplo de BSONExtraElements
  • Exemplo de erro BsonRepresentation
  • Discriminadores
  • Configuração avançada
  • Tipos abstratos ou de interface em propriedades
  • POJOs que não têm construtores sem argumentos
  • Personalização de serialização

Neste guia, você pode aprender a definir conversões de dados personalizadas entre BSON e POJOs no driver Java do MongoDB. Em nosso guia sobre POJOs, mostramos como especificar um PojoCodecProvider que contém classes que fornecem instruções sobre como converter dados para uma ou mais classes POJO e suas propriedades.

Mostramos como especificar sua conversão de dados utilizando as classes ClassModel e PropertyModel. Você também pode aprender sobre personalização mais específica na seção Configuração avançada.

Também mostramos como usar assistentes como Convenções e Anotações para especificar ações comuns de serialização.

Consulte a seção Discriminadores se quiser serializar várias classes POJO para documentos na mesma coleção.

Se você precisar implementar serialização condicional ou usar enumerações, genéricos, tipos de interface ou tipos abstratos, consulte a seção sobre configuração avançada.

Se você estiver usando apenas o comportamento predefinido para converter dados entre BSON e POJOs, você pode usar a configuração automática para o PojoCodecProvider mostrado no guia Formatos de dados do documento: POJOs.

Esta seção mostra como especificar a lógica de conversão de dados e as classes POJO com um PojoCodecProvider. O PojoCodecProvider é uma implementação da interface do CodecProvider que especifica os codecs para utilizar na conversão de dados. Use essa implementação ao executar a conversão de dados entre BSON e POJOs.

Você pode criar uma instância PojoCodecProvider utilizando o método PojoCodecProvider.builder(). Além disso, pode encadear métodos para o construtor para registrar um dos itens a seguir:

  • Classes POJO individuais

  • Nomes de pacotes que contêm classes POJO

  • Instâncias deClassModel que descrevem a lógica de conversão para uma classe POJO

O exemplo a seguir mostra como você pode especificar os POJOs em um pacote chamado "org.example.pojos" e adicione o PojoCodecProvider a um 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

Para mais informações sobre esta classe, consulte a documentação da API PojoCodecProvider.Builder.

Uma instância ClassModel armazena informações de conversão de dados sobre uma classe POJO específica. Ele contém uma lista de instâncias PropertyModel que descrevem os campos de propriedade do POJO, se deseja converter campos e, opcionalmente, Codecs para converter os campos.

Um ClassModel contém os seguintes campos:

Nome do campo
Descrição
Nome
O nome da classe POJO para associar ao ClassModel.
InstanceCreatorFactory
Contém uma nova fábrica de instâncias que cria novas instâncias do POJO. Por padrão, isso requer que o POJO tenha um construtor vazio.
PropertyModels
Contém uma lista de instâncias PropertyModel que especificam como converter dados de e para BSON para um campo no POJO.
IdPropertyModelHolder
Especifica o campo POJO que corresponde ao campo _id do documento. Opcional.
Chave do discriminador
Specifies the name of the discriminator field. Optional.
For more information about discriminators, see the Discriminators section.
Valor do discriminador
Specifies the lookup value that represents the POJO class. Optional.
For more information about discriminators, see the Discriminators section.
Sinalizador do discriminador
Especifica se deseja serializar o discriminador, desativado por padrão. Opcional.

Para obter mais informações sobre esta classe, consulte a Documentação da API ClassModel.

Para instanciar um ClassModel, utilize o método ClassModel.builder() e especifique sua classe POJO. O construtor usa reflexão para criar os metadados necessários.

ClassModel<Flower> classModel = ClassModel.builder(Flower.class).build();

Um PropertyModel armazena informações sobre como serializar e desserializar um campo específico em um documento.

O PropertyModel contém as seguintes informações:

Nome do campo
Descrição
Nome
Especifica o nome da propriedade no modelo.
Ler nome
Nome da propriedade a ser usada como chave ao serializar para BSON.
Gravar nome
Nome da propriedade a ser usada como chave ao desserializar do BSON.
Digitar dados
Contém uma instância org.bson.codecs.pojo.TypeData que descreve o tipo de dados do campo.
Codec
Especifica um codec a ser usado para codificar ou decodificar o campo. Opcional.
Verificador de serialização
Determina se deve serializar um valor usando os critérios especificados no verificador.
Acessador de propriedade
Método usado para acessar o valor da propriedade a partir do POJO.
useDiscriminator
Specifies whether to use the discriminator.
For more information about discriminators, see the Discriminators section.

Para criar um PropertyModel utilize um PropertyModelBuilder que você pode instanciar chamando o método PropertyModel.builder().

Para obter mais informações sobre esta classe, consulte PropertyModel.Builder Documentação da API.

A interface Convention contém opções de configuração que modificam o comportamento de ClassModel ou PropertyModel. Você pode especificar um Convention em uma chamada para PojoCodecProvider.Builder.conventions() ou para ClassModelBuilder.conventions().

Observação

Os construtores aplicam instâncias Convention em ordem, o que pode substituir o comportamento definido em uma instância aplicada anteriormente.

Você pode acessar as instâncias Convention definidas na biblioteca BSON a partir dos seguintes campos estáticos na classe Conventions:

Nome do campo
Descrição
ANNOTATION_CONVENTION
Habilita as anotações definidas no pacote org.bson.codecs.pojo.annotations para seu POJO. Para obter mais informações, consulte a seção Anotações.
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
Fornece uma lista vazia.
OBJECT_ID_GENERATORS
Adiciona um IdGenerator padrão que adiciona um novo ObjectId para cada ClassModel que utiliza valores de ObjectId na propriedade id.
SET_PRIVATE_FIELDS_CONVENTION
Permite que o ClassModel defina campos privados usando reflexão sem exigir um método de preparação.
USE_GETTERS_FOR_SETTERS
Habilita o uso de métodos de obtenção e preparação para campos Collection e Map se não existir nenhum método de preparação.

Você pode especificar Convenções usando um dos seguintes métodos:

Para criar uma convenção personalizada, crie uma classe que implemente a interface do Convention e substitua o método do apply() do qual você pode acessar sua instância ClassModelBuilder.

Você pode aplicar anotações aos métodos de obtenção e preparação de uma classe POJO. Estas anotações configuram o comportamento ClassModel e PropertyModel para um campo, método ou classe específico.

As seguintes anotações estão disponíveis no pacote org.bson.codecs.pojo.annotations:

Nome da Anotação
Descrição
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
Especifica que uma classe usa um discriminador. Você pode definir uma chave e um valor de discriminador personalizado.
BsonRepresentation
Especifica o tipo BSON utilizado para armazenar o valor quando diferente da propriedade POJO. Veja um exemplo de Exemplo de erro de representação BSON nesta página.
BsonId
Marca uma propriedade a ser serializada como a propriedade _id.
BsonIgnore
Marca uma propriedade a ser ignorada. Você pode configurar se deseja serializar e/ou desserializar uma propriedade.
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:
- Map<String, Object>

O seguinte trecho de código exibe uma amostra POJO chamada Product que usa várias das anotações anteriores.

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;
}
// ...
}

Dica

Ao utilizar anotações, lembre-se de especificar o Conventions.ANNOTATION_CONVENTION em seu ClassModelBuilder ou PojoCodecProvider.Builder. Por exemplo:

ClassModel<Product> classModel = ClassModel.builder(Product.class).
conventions(Arrays.asList(Conventions.ANNOTATION_CONVENTION)).build();

As anotações no exemplo POJO especificam o seguinte comportamento:

  • Faça referência ao POJO com a chave e o valor do discriminador especificado, adicionando o campo cls com o valor de "AnnotatedProduct" ao documento BSON em operações de gravação

  • Converter entre o campo e o valor POJO name e o campo e o valor BSON modelName no documento

  • Converta entre o campo e valor POJO serialNumber e o campo e valor BSON_id no documento

  • Omita o campo e valorrelatedItems ao converter dados

  • Utilize o construtor Product(String name) ao instanciar o POJO

A anotação @BsonExtraElements permite especificar um campo para desserializar dados de um documento MongoDB que não tem um mapeamento de campo POJO correspondente. Isso é útil quando seu aplicativo precisa trabalhar com dados em um esquema parcialmente definido. Você pode usar essa anotação para acessar dados de qualquer campo que não corresponda aos campos no seu POJO.

Considere uma situação em que você armazena e recupera dados de uma loja virtual usando o POJO do produto do exemplo anterior. À medida que você oferece uma variedade maior de produtos na loja, descobre que precisa de campos adicionais para descrevê-los. Em vez de mapear cada campo adicional ao POJO, você pode acessá-los a partir de um único campo anotado com @BsonExtraElements, conforme mostrado no exemplo de código a seguir:

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;
// ...

Suponha que alguém tenha adicionado campos para dimensions e weight aos dados do produto, de modo que os documentos contenham as seguintes informações:

{
"name": "MDB0123",
"serialNumber": "62e2...",
"dimensions": "3x4x5",
"weight": "256g"
}

O documento anterior recuperado usando o POJO do Product contém os seguintes dados:

ProductWithBsonExtraElements [
name=MDB0123,
serialNumber=62eb...,
relatedItems=null,
additionalInfo=Document{{dimensions=3x4x5, weight=256g}}
]

A anotação @BsonRepresentation permite que você armazene um campo de classe POJO como um tipo de dados diferente em seu banco de dados MongoDB. O exemplo de código POJO do produto na seção Anotações desta página usa @BsonRepresentation para armazenar valores String como valores ObjectId nos documentos do banco de dados.

No entanto, o uso da anotação @BsonRepresentation para converter entre tipos de dados diferentes de String e ObjectId causa a seguinte mensagem de erro:

Codec must implement RepresentationConfigurable to support BsonRepresentation

Por exemplo, o código a seguir adiciona um campo purchaseDate do tipo Long ao Product PoJo. Este exemplo tenta usar @BsonRepresentation para representar valores Long como valores DateTime no banco de dados:

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

O código anterior resulta em um erro. Em vez disso, você pode criar um codec personalizado para converter os valores purchaseDate do tipo Long para 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;
}
}

Em seguida, adicione uma instância do LongRepresentableCodec no seu CodecRegistry que contém um mapeamento entre seu Codec e o tipo de objeto Java ao qual ele se aplica. Para obter instruções sobre como registrar seu codec personalizado com o CodecRegistry, consulte o guia de codecs.

Um discriminador é uma propriedade que identifica um esquema de documento específico. A chave do discriminador identifica um campo do documento a ser usado para identificar o esquema. O valor do discriminador identifica o valor padrão do campo do documento.

Use discriminadores para instruir ao CodecProvider qual classe de objeto usar ao desserializar para diferentes classes de objetos da mesma collection. Ao serializar o POJO para uma collection do MongoDB, o codec associado define o campo de valor-chave do discriminador, a menos que especificado de outra forma nos dados da propriedade POJO.

Você pode definir e habilitar um discriminador em um POJO executando um dos seguintes procedimentos:

  • Use a anotação @BsonDiscriminator para especificar o discriminador para a classe POJO

  • ChamarenableDiscriminator(true) no ClassModelBuilder associado à classe POJO

Veja os seguintes exemplos de classes POJO que contêm anotações @BsonDiscriminator e exemplos de documentos que contêm os campos discriminadores:

@BsonDiscriminator(value="AnonymousUser", key="_cls")
public class AnonymousUser {
// class code
}
@BsonDiscriminator(value="RegisteredUser", key="_cls")
public class RegisteredUser {
// class code
}

Veja a seguir exemplos de documentos criados a partir dos POJOs anteriores em uma única collection do MongoDB:

{ "_cls": "AnonymousUser", "_id": ObjectId("<Object ID>"), ... }
{ "_cls": "RegisteredUser", "_id": ObjectId("<Object ID>"), ... }

Para serializar um POJO que inclui as propriedades classe abstrata ou tipo de interface, você deve especificar discriminadores no tipo e em todos os seus subtipos ou implementações.

Suponha que você definiu um POJO que referenciou uma classe abstrata User em um de seus campos da seguinte maneira:

public class UserRecordPojo {
private User user;
// ...
}

Se a classe abstrata User tiver subclasses FreeUser e SubscriberUser, você poderá adicionar o POJO e as classes abstratas à CodecRegistry da seguinte forma:

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));

Para obter mais informações sobre como especificar discriminadores, consulte a seção Discriminadores no presente guia.

O padrão do POJO Codecs é chamar o construtor vazio e sem argumentos. Para especificar um construtor diferente, você deve executar o seguinte em seu POJO:

  • passe a configuração ANNOTATION_CONVENTION para o seu ClassModelBuilder

  • identifique o construtor utilizando a anotação BsonCreator

Para ver um exemplo de configuração do ANNOTATION_CONVENTION, consulte o exemplo ANNOTATION_CONVENTION. Para ver um exemplo da anotação BsonCreator, consulte o exemplo de código POJO com anotação.

Por padrão, o ClassModelBuilder tenta serializar todas as propriedades não nulas em seu POJO. Se um valor de propriedade for null, a implementação padrão do PropertySerialization pulará esse campo.

Você pode personalizar seu comportamento de serialização de POJO executando um dos seguintes procedimentos:

  • Utilize a anotação @BsonIgnore para uma propriedade para sempre ignorar a serialização. Certifique-se de habilitar as anotações usando as convençõesapropriadas.

  • Crie uma classe personalizada que substitua o método shouldSerialize() da interface PropertySerialization . Especifique sua implementação personalizada para PropertyModelBuilder que você pode acessar em ClassModelBuilder.

Para obter mais informações sobre como usar a anotação @BsonIgnore em um POJO, consulte a seção deste guia sobre Anotações.

O código de exemplo a seguir mostra uma classe personalizada que implementa a interface PropertySerialization para substituir as condições padrão pelas quais se determina a serialização de um campo:

public class CourteousAgeSerialization implements PropertySerialization<Integer> {
@Override
public boolean shouldSerialize(Integer value) {
return (value < 30);
}
}

A classe anterior especifica que qualquer número inteiro maior que 29 não é serializado e, portanto, não está incluído no documento MongoDB. Suponha que você tenha aplicado este comportamento de serialização personalizada à seguinte amostra POJO:

public class BirthdayInvitation {
private String name;
private Integer age;
private LocalDateTime eventDateTime;
// ...
}

Você pode especificar a serialização personalizada adicionando a instância CourteousAgeSerialization ao PropertyModelBuilder da propriedade ClassModel associada ao campo age usando o seguinte código:

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));

Se você inserir um POJO que contém um valor maior que 29 no campo age , o documento serializado o omitirá. A declaração POJO e o documento resultante se assemelham ao seguinte:

// constructor with parameters for name, age, and eventDateTime, respectively
BirthdayInvitation invitation = new BirthdayInvitation(
"Galadriel",
7582,
LocalDateTime.of(2021, Month.JANUARY, 18, 30, 0)
);

Como o valor do campo age é maior que 29, o documento serializado é semelhante ao seguinte:

{ "_id" : ObjectId("..."), "eventDateTime" : ..., "name" : "Galadriel" }

Você pode usar o POJO Codec para serializar classes que contêm propriedades genéricas se elas atenderem aos seguintes critérios:

  • Contém apenas parâmetros de tipos concretos delimitados

  • Se ele ou qualquer um dos seus campos fizer parte de uma hierarquia de classe, o POJO de nível superior não conterá nenhum parâmetro de tipo

O ClassModelBuilder inspeciona e salva parâmetros de tipo concreto para trabalhar ao redor do apagamento do tipo. Não é possível serializar classes que contêm propriedades genéricas sem parâmetros de tipo concreto, pois a JVM remove as informações do parâmetro de tipo.

Para salvar parâmetros de tipo, você pode implementar a interface do PropertyCodecProvider para especificá-los como tipos genéricos definidos em um POJO. Os seguintes trechos de código mostram um exemplo de implementação do PropertyCodecProvider que adiciona compatibilidade de serialização à classe Guava Optional.

Suponha que você queira serializar o seguinte POJO com campos Optional:

public class ApplicationUser {
private Optional<Address> optionalAddress;
private Optional<Subscription> optionalSubscription;
// ...
}

Você pode utilizar a seguinte implementação do PropertyCodecProvider para recuperar seu codec personalizado. Essa implementação usa a interface doTypeWithTypeParameters para acessar as informações do tipo.

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;
}
}
}

Registre seu OptionalPropertyCodecProvider no seu PojoCodecProvider e no pacote que contém seu POJO como segue:

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

Para obter mais informações sobre os métodos e as classes mencionadas nesta seção, consulte a seguinte documentação da API:

Para obter mais informações sobre genéricos e parâmetros de tipo, consulte o guia da linguagem Java sobre como invocar e instanciar um tipo genérico.

Nas versões 4.5 e posterior do driver, o PojoCodecProvider não inclui mais um codec para converter tipos enum. Certifique-se de registrar um codec para tiposenum se precisar de um, como o do registro de codec padrão.

Consulte a documentação sobre o registro de codec padrão para obter mais informações sobre como registrar os codecs que ele inclui.

Voltar

Formato de dados do documento: Registros