MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Spring Data MongoDB – Índices, Anotações e Conversores

1. Visão geral


Neste tutorial, exploraremos alguns dos principais recursos do Spring Data MongoDB – indexação, anotações comuns e conversores.

2. Índices

2.1. @Indexado


Esta anotação marca o campo como indexado no MongoDB:
@QueryEntity
@Document
public class User {
    @Indexed
    private String name;
    
    ... 
}

Agora que o nome campo está indexado – vamos dar uma olhada nos índices no shell do MongoDB:
db.user.getIndexes();

Aqui está o que obtemos:
[
    {
        "v" : 1,
        "key" : {
             "_id" : 1
         },
        "name" : "_id_",
        "ns" : "test.user"
    }
]

Podemos nos surpreender que não haja sinal do nome campo em qualquer lugar!

Isso ocorre porque, a partir do Spring Data MongoDB 3.0, a criação automática de índice está desativada por padrão .

Podemos, no entanto, alterar esse comportamento substituindo explicitamente autoIndexCreation() método em nosso MongoConfig :
public class MongoConfig extends AbstractMongoClientConfiguration {

    // rest of the config goes here

    @Override
    protected boolean autoIndexCreation() {
        return true;
    }
}

Vamos verificar novamente os índices no shell do MongoDB:
[
    {
        "v" : 1,
        "key" : {
             "_id" : 1
         },
        "name" : "_id_",
        "ns" : "test.user"
    },
    {
         "v" : 1,
         "key" : {
             "name" : 1
          },
          "name" : "name",
          "ns" : "test.user"
     }
]

Como podemos ver, desta vez, temos dois índices – um deles é _id – que foi criado por padrão devido ao @Id anotação e a segunda é nosso nome campo.

Como alternativa, se usarmos o Spring Boot, podemos definir o spring.data.mongodb.auto-index-creation propriedade para true .

2.2. Criar um índice programaticamente


Também podemos criar um índice programaticamente:
mongoOps.indexOps(User.class).
  ensureIndex(new Index().on("name", Direction.ASC));

Agora criamos um índice para o campo nome e o resultado será o mesmo da seção anterior.

2.3. Índices compostos


O MongoDB suporta índices compostos, onde uma única estrutura de índice contém referências a vários campos.

Vamos ver um exemplo rápido usando índices compostos:
@QueryEntity
@Document
@CompoundIndexes({
    @CompoundIndex(name = "email_age", def = "{'email.id' : 1, 'age': 1}")
})
public class User {
    //
}

Criamos um índice composto com o email e idade Campos. Vamos agora verificar os índices reais:
{
    "v" : 1,
    "key" : {
        "email.id" : 1,
        "age" : 1
    },
    "name" : "email_age",
    "ns" : "test.user"
}

Observe que um DBRef campo não pode ser marcado com @Index – esse campo só pode fazer parte de um índice composto.

3. Anotações comuns

3.1. @Transient


Como seria de esperar, esta simples anotação exclui a persistência do campo no banco de dados:
public class User {
    
    @Transient
    private Integer yearOfBirth;
    // standard getter and setter

}

Vamos inserir o usuário com o campo de configuração yearOfBirth :
User user = new User();
user.setName("Alex");
user.setYearOfBirth(1985);
mongoTemplate.insert(user);

Agora, se olharmos o estado do banco de dados, veremos que o arquivo yearOfBirth não foi salvo:
{
    "_id" : ObjectId("55d8b30f758fd3c9f374499b"),
    "name" : "Alex",
    "age" : null
}

Então, se consultarmos e verificarmos:
mongoTemplate.findOne(Query.query(Criteria.where("name").is("Alex")), User.class).getYearOfBirth()

O resultado será null .

3.2. @Campo


@Campo indica a chave a ser usada para o campo no documento JSON:
@Field("email")
private EmailAddress emailAddress;

Agora endereço de e-mail será salvo no banco de dados usando a chave email:
User user = new User();
user.setName("Brendan");
EmailAddress emailAddress = new EmailAddress();
emailAddress.setValue("[email protected]");
user.setEmailAddress(emailAddress);
mongoTemplate.insert(user);

E o estado do banco de dados:
{
    "_id" : ObjectId("55d076d80bad441ed114419d"),
    "name" : "Brendan",
    "age" : null,
    "email" : {
        "value" : "[email protected]"
    }
}

3.3. @PersistenceConstructor e @Value


@PersistenceConstructor marca um construtor, mesmo aquele protegido por pacote, para ser o construtor primário usado pela lógica de persistência. Os argumentos do construtor são mapeados por nome para os valores de chave no DBObject recuperado .

Vamos dar uma olhada neste construtor para o nosso User aula:
@PersistenceConstructor
public User(String name, @Value("#root.age ?: 0") Integer age, EmailAddress emailAddress) {
    this.name =  name;
    this.age = age;
    this.emailAddress =  emailAddress;
}

Observe o uso do padrão Spring @Value anotação aqui. É com a ajuda desta anotação que podemos usar o Spring Expressions para transformar o valor de uma chave recuperado do banco de dados antes de ser usado para construir um objeto de domínio. Esse é um recurso muito poderoso e altamente útil aqui.

Em nosso exemplo, se idade não estiver definido, será definido como 0 por padrão.

Vamos agora ver como funciona:
User user = new User();
user.setName("Alex");
mongoTemplate.insert(user);

Nosso banco de dados ficará:
{
    "_id" : ObjectId("55d074ca0bad45f744a71318"),
    "name" : "Alex",
    "age" : null
}

Portanto, a idade campo é nulo , mas quando consultamos o documento e recuperamos idade :
mongoTemplate.findOne(Query.query(Criteria.where("name").is("Alex")), User.class).getAge();

O resultado será 0.

4. Conversores


Vamos agora dar uma olhada em outro recurso muito útil no Spring Data MongoDB – conversores, e especificamente no MongoConverter .

Isso é usado para lidar com o mapeamento de todos os tipos Java para DBObjects ao armazenar e consultar esses objetos.

Temos duas opções – podemos trabalhar com MappingMongoConverter – ou SimpleMongoConverter em versões anteriores (isso foi preterido no Spring Data MongoDB M3 e sua funcionalidade foi movida para MappingMongoConverter ).


Ou podemos escrever nosso próprio conversor personalizado. Para fazer isso, precisaríamos implementar o Converter interface e registre a implementação em MongoConfig.

Vejamos um exemplo rápido . Como vimos em algumas saídas JSON aqui, todos os objetos salvos em um banco de dados possuem o campo _class que é salvo automaticamente. Se, no entanto, quisermos pular esse campo específico durante a persistência, podemos fazer isso usando um MappingMongoConverter .

Primeiro – aqui está a implementação do conversor personalizado:
@Component
public class UserWriterConverter implements Converter<User, DBObject> {
    @Override
    public DBObject convert(User user) {
        DBObject dbObject = new BasicDBObject();
        dbObject.put("name", user.getName());
        dbObject.put("age", user.getAge());
        if (user.getEmailAddress() != null) {
            DBObject emailDbObject = new BasicDBObject();
            emailDbObject.put("value", user.getEmailAddress().getValue());
            dbObject.put("email", emailDbObject);
        }
        dbObject.removeField("_class");
        return dbObject;
    }
}

Observe como podemos facilmente atingir a meta de não persistir _class removendo especificamente o campo diretamente aqui.

Agora precisamos registrar o conversor personalizado:
private List<Converter<?,?>> converters = new ArrayList<Converter<?,?>>();

@Override
public MongoCustomConversions customConversions() {
    converters.add(new UserWriterConverter());
    return new MongoCustomConversions(converters);
}

É claro que também podemos obter o mesmo resultado com a configuração XML, se precisarmos:
<bean id="mongoTemplate" 
  class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg name="mongo" ref="mongo"/>
    <constructor-arg ref="mongoConverter" />
    <constructor-arg name="databaseName" value="test"/>
</bean>

<mongo:mapping-converter id="mongoConverter" base-package="org.baeldung.converter">
    <mongo:custom-converters base-package="com.baeldung.converter" />
</mongo:mapping-converter>

Agora, quando salvamos um novo usuário:
User user = new User();
user.setName("Chris");
mongoOps.insert(user);

O documento resultante no banco de dados não contém mais as informações de classe:
{
    "_id" : ObjectId("55cf09790bad4394db84b853"),
    "name" : "Chris",
    "age" : null
}