Spring Data na prática: o que é e seus principais subprojetos

Neste artigo você vai ver:

Que tal nos aventurar pelo Spring Data

Neste artigo vamos entender o que é o Spring Data e seus subprojetos. Além disso, vamos ver como eles podem facilitar as nossas vidas, agilizando todo o processo de persistência de dados.

Caso você já tenha alguma experiência com Hibernate provavelmente já deve ter ouvido do Spring Data ou até mesmo usado ele. Aqui vamos ver um pouco da teoria, sua utilização e algumas dicas, com base em problemas que enfrentei durante meu dia a dia na Zup.

Afinal, o que é Spring Data?

A logo do Spring Data é uma forma hexagonal verde clara com o desenho de uma folha no meio.
Logo do Spring Data

O Spring Data é o modelo de programação dentro do Spring Framework para acesso e manipulação de dados.

Chegando com a intenção de facilitar a configuração e utilização com o seu banco de dados, seja ele relacional ou não, o Spring Data traz vários recursos bacanas para acelerar o nosso desenvolvimento. Por exemplo, a configuração padronizada, onde devemos colocar apenas algumas propriedades e ele já vai saber o que fazer. 

Outro recurso que gosto muito é a criação de query pela assinatura do método, falando desse jeito pode ficar um pouco confuso, mas na prática acaba sendo muito simples de utilizar. 

Subprojetos do Spring

Tendo diversos subprojetos, o Spring Data quase sempre satisfaz toda a nossa necessidade de persistência, trazendo operações CRUD, onde conseguimos criar, ler, alterar e deletar dados, tudo isso já pronto. Ele também conta com Paginação e Ordenação. 

Vamos explorar um pouco de três subprojetos do Spring Data: 

  • Spring Data Jpa;
  • Spring MongoDB;
  • Spring Data Redis. 

O Jpa é voltado para bancos relacionais, como Mysql, MariaDb etc. Já o MongoDb é um banco de dados orientado a documentos. Por último, o Redis é um banco de dados em memória. 

Uma das diferenças logo de início é o Spring Data Redis, que diferente dos outros dois projetos, não tem um template fixo pelo Spring. Com ele temos o Jedis e Lettuce, onde você pode ver as suas vantagens e desvantagens aqui (em inglês).

Mapeamento de objetos

O mapeamento dos nossos objetos que vão ser persistidos segue o padrão de utilização de anotações entre os três projetos.

Vamos fazer uma comparação entre eles para vermos as semelhanças, mesmo utilizando bancos diferentes.

  • MongoDb
 @Document
public class Pessoa{
 
 @Id
 private BigInteger id;
 
 private String nome;
 
 private int idade;
 
 //Metodos get/set e construtores
}
  • Redis
@RedisHash("Pessoa")
public class Pessoa{
 
 @Id
 private BigInteger id;
 
 private String nome;
 
 private int idade;
 
 //Metodos get/set e construtores
}
  • Jpa (MariaDb)
@Entity
public class Pessoa{
 
 @Id
 private BigInteger id;
 
 private String nome;
 
 private int idade;
 
 //Metodos get/set e construtores
}

Podemos ver que na hora de mapear temos muitas semelhanças, principalmente na anotação @Id, a qual indica qual o atributo vai ser o id na classe.

Já a outra anotação, que varia de projeto para projeto, indica que a classe vai ser gerenciada pelo Spring Data. Com isso ele vai saber manipular o objeto, inserindo, buscando, alterando e excluindo, as funções de CRUD, juntamente com a paginação.

Após o mapeamento, a manipulação dos objetos a serem persistidos são semelhantes, seguindo o padrão de Repository. Para isso, basta a gente criar a nossa interface de implementação.

Vamos dar uma olhada em como fazer algumas consultas utilizando o Spring Data Jpa, um dos subprojetos mais famosos do Spring Data.

Utilização com jpa para banco relacionais

Hora de botar a mão na massa! (no teclado na verdade). 

Para vermos o Spring Data na prática, vamos utilizar Java 11, com Spring Boot e o banco de dados vai ser MariaDB.

Primeiro, após criarmos o nosso projeto Spring Boot (utilizei o Spring-Initializr), vamos adicionar as dependências do nosso projeto, sendo elas:

  • Spring Data Jpa, que ficará responsável por fazer a configuração com bancos relacionais e facilitar a utilização; 
  • Spring Web, para fazermos um serviço web qual consiga receber e devolver dados;
  • Por último, mas não menos importante, o driver do MariaDb, para que o Spring Data o reconheça. 
<dependency>
 <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
 <groupId>org.mariadb.jdbc</groupId>
 <artifactId>mariadb-java-client</artifactId>
 <scope>runtime</scope>
 </dependency>

Configurações

Agora que as nossas dependências foram baixadas no projeto, vamos colocar as configurações para o Spring Data saber fazer a conexão com o nosso banco. 

Para isso, vamos na pasta Resources dentro do nosso main, lá vamos encontrar um arquivo chamado application.properties. Ali vamos poder colocar algumas propriedades, por exemplo, a url do nosso banco, o nome do usuário e sua senha. Também temos que passar o driver para que o Spring Data saiba montar as nossas consultas.

Além disso, vamos ter outras duas configurações: uma para dizer se o Spring vai gerenciar a evolução do nosso banco ou não e a outra para definir a maneira de como ele vai fazer esse gerenciamento, utilizando update, create,  create-drop, validate, none. 

Vamos conhecer melhor essas cinco configurações de gerenciamento do banco de dados, elas são gerenciadas utilizando DLL a seguir temos nossas opções: 

  • update – sempre que a aplicação iniciar ele verifica se suas classes entidades estão de acordo com o banco. Caso não esteja, vai ser feito um update no banco adicionando novas colunas na tabela dessa entidade. Vale lembrar que o update não remove ou renomeia colunas. Por isso, não é recomendado de jeito algum utilizar o Spring Data para gerenciar a evolução do seu banco, para isso existem ferramentas próprias, por exemplo, o FlyWay.
  • create – sempre que iniciar sua aplicação o Spring Data vai apagar tudo e recriar novamente.
  • create-drop – bem semelhante ao create, mas sempre que a aplicação é parada ele apaga tudo que foi criado.
  • validate ele faz uma validação sempre que a aplicação inicia e verifica se o banco bate com as suas classes de entidade.
  • nonebasicamente é nenhuma das opções acima, ou seja, significa que você não quer que o Spring Data faça alterações no seu banco. Essa opção é a recomendada para produção, para que não ocorra problemas do Spring modificar seu banco.
spring.datasource.url=jdbc:mariadb://localhost:3306/banco
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=true

Criando uma classe de entidade

Agora vamos criar a nossa classe de entidade, a qual vai representar uma tabela no nosso banco de dados. Como deixamos o Spring gerenciar a criação das tabelas pra gente, vamos focar apenas no código.

Vamos criar uma entidade para representar uma pessoa, contendo apenas três atributos: id único gerado aleatoriamente, nome e idade. Além disso, vamos adicionar algumas anotações do Spring Data.

@Entity
public class Pessoa {

 @Id
 private String id = UUID.randomUUID().toString();

 private String nomeCompleto;
 private String email;

 public Pessoa() {
 }

 public Pessoa(String id, String nomeCompleto, String email) {
  super();
  this.id = id;
  this.nomeCompleto = nomeCompleto;
  this.email = email;
 }

 public String getId() {
  return id;
 }

 public String getNomeCompleto() {
  return nomeCompleto;
 }

 public String getEmail() {
  return email;
 }

}

Caso você já tenha uma familiaridade com Java, parece uma classe bem comum de entidade, apenas com a diferença das anotações. Por falar das anotações, vamos rever uma por uma:

  • Entity –  Essa anotação diz para o Spring que a classe é uma entidade do Spring Data e que ela deve ser gerenciada conforme a configuração.
  • Id – Diz para o nosso banco que esse atributo vai ser a nossa chave primária.

Uma coisa que talvez tenha estranhado é a utilização de dois construtores, um vazio e um com os atributos da classe. O vazio é necessário para o Spring saber fazer reflection possibilitando o objeto “olhar” e modificar ele mesmo, já o construtor com os atributos é para atribuir os valores no momento da criação do objeto, pois como podem ver, não temos nenhum método set na nossa classe.

Com a nossa entidade criada, podemos partir para a criação da nossa classe que vai ser responsável por persistir os dados no nosso banco.

Antes de conhecer Hibernate e Spring Data, eu criava todos os métodos de consultas no banco manualmente, até mesmo as querys. Agora com o Spring Data acaba sendo tão simples que na primeira vez eu até duvidei que funcionava. 

Voltando ao nosso exemplo, para criar nossa classe vamos criar uma interface chamada PessoaRepository.

public interface PessoaRepository extends JpaRepository<Pessoa, String> {
}

Vimos que criamos uma interface vazia apenas estendendo de outra interface chamada JpaRepository passando como parâmetro dois atributos. O primeiro é a nossa entidade que vai ser gerenciada e o segundo é o tipo de dado da chave primária que definimos, que no caso vai ser uma String.

Implementação

Nesse momento você deve estar pensando “Onde está a implementação?”. A implementação está toda nessa interface que criamos, caso você abra ela, vai poder ver alguns métodos padrão que já vem criado, vamos fazer uso de alguns deles para ver como funciona.

No código abaixo tem um exemplo de como podemos salvar uma pessoa, apenas utilizando o método save, passando como parâmetro o objeto da nossa entidade para ser salvo.

PessoaRepository repository;
Pessoa pessoa = pessoaRequest;
repository.save(pessoa);

Apenas fazendo isso a gente já consegue persistir um dado de pessoa no nosso banco. Caso a gente queira realizar buscas, podemos utilizar os métodos findAll para retornar todos os dados no banco ou podemos buscar apenas um, utilizando o findById, onde é buscado pelo atributo id.

Dicas de consultas no Spring Data

Essas são apenas as funções básicas do Spring Data, agora iremos ver algumas dicas de utilização para facilitar ainda mais as suas consultas. 

Vamos supor que precisamos achar alguma pessoa pelo e-mail dela. Porém, por padrão, a nossa interface JpaRepository não traz essas consultas, mas nós traz a facilidade de montar elas. Vamos ver como ficaria a consulta na nossa interface criada.

Pessoa findByEmail(String email);

Apenas digitando findBy{nome do atributo} você consegue buscar uma pessoa pelo nome do atributo selecionado.

Na documentação do Spring Data – Jpa tem uma lista de comandos que ele aceita. Normalmente eles já dão conta do trabalho, mas se por acaso você precisar de uma query bem específica, pode utilizar a anotação @Query, onde você pode passar a sua consulta.

@Query("select nome from Pessoa where email = :email")
 String buscaNomePeloEmail(String email);

Dessa forma o nome do método não importa, pois o que vai prevalecer na hora da consulta vai ser a anotação. 

Outra coisa que fizemos diferente, é que ao invés de retornar um objeto Pessoa, agora retornamos apenas uma String, contendo o nome dessa pessoa. Dessa forma a gente consegue ter um controle maior nas consultas, evitando buscar dados desnecessários. 

Mas vamos supor que com a mesma query algumas vezes a gente precise apenas do nome ou apenas do e-mail, teríamos que ter duas consultas? Talvez sim, se utilizarmos essa maneira, mas o Spring Data tem a capacidade de utilizar projection, para fazer consultas dinâmicas, possibilitando com a mesma consulta, a gente possa ter resultados diferentes, dependendo da nossa necessidade. Para utilizar, basta criarmos interfaces para cada tipo de retorno que a gente queira:

public interface NomeApenas {
 String getNome();
}


public interface EmailApenas {
 String getEmail();
}

Como podem ver, temos duas interfaces novas, cada uma com apenas um método de busca. Indo para o nosso repository, vamos implementar a nossa consulta dinâmica.

<T> Collection<T> findByNome(String nome, Class<T> type);

Usamos da classe genérica de Java juntamente com uma Collection, a nossa consulta pelo nome traz um novo parâmetro, o qual vai definir o tipo de retorno da consulta, por exemplo:

Pessoa joao = repository.findByNome("Joao", EmailApenas.class);

Dessa forma podemos ter apenas uma consulta genérica no nosso repository, e a gente define o tipo de retorno criando interfaces. Isso é muito útil caso você tenha um banco de dados grande e precise fazer várias consultas na mesma tabela.

Conclusão

O Spring Data nos traz diversas facilidades para o dia a dia, mas temos que saber o que estamos fazendo. Sempre que for utilizar algo novo, busque saber exatamente o que isso faz, pois ele vai mexer com o nosso banco. Afinal, não queremos deletar ou alterar dados sem querer, né?

Terminamos a nossa aventura pelo Spring Data, mas não pense que acabou! Ainda há muito o que ser estudado, existem alguns casos de erros que eu poderia criar outro artigo só para eles. Ah, a documentação do Spring Data é muito boa e completa. Recomendo a leitura!

Mas com isso te convido para aprender sobre tecnologia com a gente aqui no blog, no ZupCast e em nosso canal no YouTube. Além disso, melhor que nossos conteúdos, só sendo zupper para aí sim crescer exponencialmente. Por isso, confira nossas vagas!

Quer saber mais sobre o Spring Data ou ficou com alguma dúvida? Então conta pra gente nos comentários! 

Artigo Spring Data jpa na prática onde vemos Closeup de mão de funcionário negro de programação trabalhando remoto de casa.
Foto Ulysses Uchoa
Desenvolvedor back-end pleno
Olá, meu nome é Ulysses e sou desenvolvedor, e gosto de tudo um pouco desde jogar basquete a virar noites resolvendo desafios de programação.

Artigos relacionados

Capa do artigo em foto com duas pessoas escrevendo códigos em frente a dois notebooks.
Back-End
Postado em:
Imagem capa do conteúdo sobre Framework NestJS com a foto de uma mulher de cabelos trançados de costas de frente para um computador com códigos.
Back-End
Postado em:
Imagem capa do conteúdo sobre testes unitários, onde uma pessoa branca está em pé, segurando um notebook aberto dentro de um data center.
Back-End
Postado em:

Este site utiliza cookies para proporcionar uma experiência de navegação melhor. Consulte nossa Política de Privacidade.