Loading ...

O Blog de Tecnologia da Geofusion

Facilitando o acesso a dados com Spring-Data MongoDB

Facilitando o acesso a dados com Spring-Data MongoDB

Introdução


Com o crescimento da utilização de diferentes banco de dados NoSQL nas aplicações para resolver o problema com a melhor estrutura de dados possível, a chamada persistência poliglota, surgiu a necessidade de tornar mais simples a comunicação da sua aplicação com os bancos de dados existentes. Tudo isso para que o desenvolvedor possa focar no problema em questão. Nesse artigo iremos tratar do framework Spring-Data, uma parte integrante do ecossistema Spring, mas especificamente do Spring-Data MongoDB para a comunicação com um dos mais conhecidos bancos de dados NoSQL do momento.

 

O que é o MongoDB?


Para o leitor não familiarizado com os bancos de dados NoSQL, o MongoDB é um banco de dados orientado a documentos, ou seja, cada registro de informação é considerado um objeto (ou uma unidade), que nesse caso é chamado de Document (documento).
No MongoDB os documentos são armazenados no formado BSON (Binary JSON) e toda a manipulação de dados é realizada através de JavaScript e consequentemente utiliza JSON.
Basicamente os Documents são armazenados em conjuntos, as chamadas Collections. No exemplo abaixo, executado no console do MongoDB, estamos inserindo um novo Document referente a um cliente na Collection customers:

 

db.customers.insert({
    "name": "Alexandre Arcanjo de Queiroz",
    "age": 29,
    "sex": "Male",
    "address": {
        "street": "Avenida Paes de Barros, 1000",
        "city": "São Paulo",
        "state": "SP",
        "country": "Brazil"
    }
});

 

MongoDB x Relational Model


Você poderia associar de modo simplista Collections com o conceito de tabela em um modelo relacional e Documents com registros ou tuplas, mas lembrando que não são a mesma coisa. Um livro introdutório que explica de modo didático os principais tipos de bancos de dados NoSQL, inclusive os bancos de dados orientados a documentos, é NoSQL Distilled de Martin Fowler e Pramod J. Sadalage, que possui tradução para o português com o título de NoSQL Essencial. Um livro muito bom para iniciar os estudos sobre persistência poliglota.

 

Spring-Data MongoDB: Let’s begin!


Considerando que você esteja trabalhando em uma aplicação Java com Maven e Spring Framework, o primeiro passo é adicionar a dependência do Spring-Data MongoDB e o MongoDB Java Driver em seu pom.xml.

 

<!-- Spring-Data Mongodb -->
<dependency>
   <groupId>org.springframework.data</groupId>
   <artifactId>spring-data-mongodb</artifactId>
   <version>1.9.1.RELEASE</version>
</dependency>

<!-- MongoDB Java Driver -->
<dependency>
   <groupId>org.mongodb</groupId>
   <artifactId>mongo-java-driver</artifactId>
   <version>2.14.0</version>
</dependency>


Nesse exemplo estamos utilizando a versão 1.9.1.RELEASE do Spring-Data MongoDB com o Spring Framework 4.2.5.RELEASE. Nesse caso o Spring-Data MongoDB vai utilizar a versão 2.14.0 do MongoDB Java Driver para se comunicar com o MongoDB.

O próximo passo é configurar o MongoDB na aplicação. Vamos primeiramente configurar a conexão com o MongoDB a partir da classe MongoFactoryBean, que cria um objeto que representa o próprio MongoDB.

Nesse artigo vamos configurar o Spring via XML, mas nada impede de realizar a mesma configuração a partir de Java Config. Então, adicione o MongoFactoryBean no applicationContext.xml da seguinte forma:

 

<bean id="mongo" class="org.springframework.data.mongodb.core.MongoFactoryBean">
    <property name="host" value="${mongodb.host}" />
    <property name="port" value="${mongodb.port}" />
</bean>


Nossa aplicação está configurada com o PropertyPlaceholderConfigurer do Spring, então no start da aplicação o Spring vai resolver ${mongodb.host} e ${mongodb.port} com os valores armazenados no arquivo de properties configurado. Se você está rodando o MongoDB na máquina local na porta default o mongodb.host será localhost e mongodb.port será 27017 como no exemplo abaixo:

 

# arquivo application.properties lido pelo PropertyPlaceholderConfigurer
mongodb.host=localhost
mongodb.port=27017
mongodb.database=application


A aplicação em si vai utilizar um template para executar as operações de seleção, inserção, atualização e exclusão, de modo similar ao JdbcTemplate, mas no nosso caso é o MongoTemplate. O bean do MongoTemplate deve ser configurado desse modo:

 

<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg name="mongo" ref="mongo" />
    <constructor-arg name="databaseName" value="${mongodb.database}" />
</bean>


Com as configurações feitas no XML do Spring agora podemos receber o MongoTemplate via injeção de dependências da mesma forma que qualquer bean do Spring: vamos criar um DAO para encapsular a lógica de acesso a dados para a entidade Customer. Um primeiro esboço seria:

 

@Repository("customerRepository")
public class CustomerDAO implements CustomerRepository {
    private MongoTemplate mongoTemplate;

    @Autowired
    public CustomerDAO(final MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    // TODO Implementar métodos
}


Recebemos o MongoTemplate via construtor a partir da anotação Autowired, isso significa implicitamente que o MongoTemplate é obrigatório para a construção do bean CustomerDAO, mas isso não quer dizer que não podemos injetar via field ou via setter.
Com o template “em mãos” podemos agora salvar nossas entidades no MongoDB facilmente a partir dos métodos do MongoTemplate. Vamos agora codificar nossa entidade Customer, que no fim das contas vai ser um Document no MongoDB. A classe Customer seria mais ou menos assim:

public class Customer implements Serializable {

    private String id;
    private String name;
    private Integer age;
    private String sex;
    private Address address;

    public Customer() { }

    // TODO omitindo getters, setters, equals, hashCode e toString
}

 

O MongoDB suporta Embedded Documents (ou subdocuments) que no nosso exemplo é a classe Address. Veja:

 

public class Address implements Serializable {

    private String street;
    private String city;
    private String state;
    private String country;

    public Address() { }

    // TODO omitindo getters, setters, equals, hashCode e toString
}


Existem duas estratégias para a criação de entidades para o MongoTemplate: com ou sem configuração via annotations. A configuração via annotations permite que os métodos do MongoTemplate possam descobrir informações sobre a Collection e Id do Document a partir do próprio objeto e sobrescrever as convenções que o Spring-Data MongoDB segue.  Veja um exemplo utilizando a configuração via annotations:

 

@Document(collection="customers")
public class Customer implements Serializable {

    @Id
    private String id;

    ...
}


Já a configuração sem annotations se vale da convenção sobre configuração para Collections e para Ids: o nome da Collection é o mesmo nome da classe mas em minúsculo e o id da entidade deverá se chamar obrigatoriamente id. No caso de ids gerados automaticamente pelo MongoDB, o campo deverá ser do tipo String ou BigDecimal.
Há também a possibilidade de especificar o nome da Collection a ser usada nos métodos do MongoTemplate.


As duas estratégias são válidas: a primeira favorece simplicidade de código e a segunda permite distribuir a mesma classe de entidade como uma classe de API, sem que os clientes da API saibam que aquela classe modela um Document do MongoDB, por exemplo.
Agora nosso CustomerDAO completo ficaria dessa forma:

 

@Repository("customerRepository")
public class CustomerDAO implements CustomerRepository {

    private MongoTemplate mongoTemplate;

    @Autowired
    public CustomerDAO(final MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    @Override
    public void create(final Customer customer) {
        this.mongoTemplate.insert(customer);
    }

    @Override
    public void update(final Customer customer) {
        this.mongoTemplate.save(customer);
    }

    @Override
    public void delete(final String id) {
        final Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(id));
        this.mongoTemplate.remove(query, Customer.class);
    }

    @Override
    public Customer findById(final String id) {
        final Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(id));
        return this.mongoTemplate.findOne(query, Customer.class);
    }

}

 

Podemos então utilizar o CustomerDAO onde for necessário. O teste de integração abaixo mostra um fluxo básico da utilização de nosso “repositório de clientes”:

 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:applicationTestContext.xml"})
public class CustomerRepositoryIT {

    @Autowired
    private CustomerRepository customerRepository;

    @Test
    public void test() {
        // Create
        final Address address = new Address();
        address.setStreet("Avenida Paes de Barros, 1000");
        address.setCity("São Paulo");
        address.setState("SP");
        address.setCountry("Brasil");
        final Customer customer = new Customer();
        customer.setName("Alexandre Arcanjo de Queiroz");
        customer.setAge(29);
        customer.setSex("M");
        customer.setAddress(address);
        customerRepository.create(customer);

        // Read
        final Customer customer2 = customerRepository.findById(customer.getId());

        // Update
        customer2.getAddress().setStreet("Rua Apucarana, 500");
        customerRepository.update(customer2);

        // Delete
        customerRepository.delete(customer2.getId());
    }
}

 

O objetivo desse artigo foi mostrar a configuração básica do Spring-Data MongoDB com um exemplo de DAO para a entidade cliente, ilustrando os principais métodos do MongoTemplate. É possível simplificar ainda mais o desenvolvimento da camada de persistência delegando a tarefa da criação do DAO (isso mesmo, do DAO!) para o próprio Spring a partir da interface MongoRepository, mas esse tópico fica para um próximo artigo.

Geofusion.tech

2016 todos os direitos reservados.

www.geofusion.com.br