Menu Docs

Página inicial do DocsDesenvolver aplicaçõesMongoDB DriversDriver 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
  • 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 MongoDB. No 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ções mais específicas na seção sobre Configuração avançada.

Também mostramos como usar auxiliares, 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 collection.

Se você precisar implementar serialização condicional ou usar enums, genéricos, tipos de interface ou tipos abstratos, consulte a seção 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 obter mais informações sobre esta classe, consulte o PojoCodecProvider.Builder Documentação da API.

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
Especifica o nome do campo discriminador. Opcional.
Para obter mais informações sobre discriminadores, consulte a seção Discriminadores .
Valor do discriminador
Especifica o valor de pesquisa que representa a classe POJO. Opcional.
Para obter mais informações sobre discriminadores, consulte a seção Discriminadores .
Sinalizador do discriminador
Especifica se deseja serializar o discriminador, desativado por padrão. Opcional.

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

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
Especifica se o discriminador deve ser usado.
Para obter mais informações sobre discriminadores, consulte a seção Discriminadores .

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 o 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
Define os seguintes valores padrão para as instâncias ClassModel e PropertyModel:
- Chave do discriminador para _t
- Valor discriminador para o nome de tipo simples ClassModel
- Campo de ID para _id para cada PropertyModel.
DEFAULT_CONVENTIONS
Habilita as seguintes convenções:
- 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 em org.bson.codecs.pojo.annotations pacote:

Nome da Anotação
Descrição
BsonCreator
Marca um construtor público ou um método estático público como criador para novas instâncias da classe. Você deve anotar todos os parâmetros no construtor com as anotações BsonProperty ou BsonId.
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.
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
Especifica um nome de campo de documento personalizado ao converter o campo POJO para BSON. Você pode incluir um discriminador para serializar POJOs aninhados no campo.
BsonExtraElements

Especifica o campo POJO no qual desserializar todos os elementos que não são mapeados para um campo. O campo POJO deve ter um dos seguintes tipos:

Dica

Veja também:

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

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 para a loja, descobre que precisa de campos adicionais para descrevê-los. Em vez de mapear cada campo adicional para o 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}}
]

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 deste guia sobre Discriminadores.

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 POJO com código de 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ções apropriadas.

  • 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.

← Formato de dados do documento: Registros
Codecs →