emhk.

Repositórios com o Spring Data JPA

17 de Aug, 2023Emanoel Henrick

Uma das coisas mais legais do Spring Data JPA, é a implementação em tempo de execução dos repositórios, isto é, para algumas funções básicas de CRUD, basta uma interface extender de JpaRepository passando o tipo da entidade e o tipo da chave primária nos generics e o Spring vai automaticamente criar um repositório com métodos como save(), findAll() e etc:

public interface UserRepository extends JpaRepository<User, Long> { }

É possível ainda usar palavras reservadas para criar métodos personalizados que serão gerados seguindo o nome do método, por exemplo, se você quiser listar usuários pelo grupo basta adicionar group como método:

public interface UserRepository extends JpaRepository<User, Long> { List<User> group(String group); }

Dessa forma, será implementado um método que devolve uma lista de usuários com o mesmo grupo. Para deixar o método mais legível, podemos usar algumas palavras reservadas como o findBy:

public interface UserRepository extends JpaRepository<User, Long> { List<User> findByGroup(String group); }

Caso queira procurar um usuário por um grupo que contenha alguma palavra no nome, podemos usar o sufixo reservado Containing:

public interface UserRepository extends JpaRepository<User, Long> { List<User> findByGroupContaining(String word); }

Dessa forma ele retorna uma lista de usuários que fazem parte de um grupo que contém a String passada no método no nome. Existem diversas palavras reservadas que podem ser usadas para a criação automática de métodos mas conforme a complexidade da aplicação vai aumentando, o nome dos métodos vão ficando inviáveis e se tornando uma má prática, por isso só use caso não precise de tantos parâmetros, nesses casos é melhor implementar na mão como vamos ver a seguir.

Usando JPQL para fazer queries no banco de dados

Anotando um método com @Query(X), e passando em X o JPQL, é possível criar métodos personalizados para consulta e escrita no bando de dados, sobrescrevendo a implementação do método:

public interface UserRepository extends JpaRepository<User, Long> { @Query("from User where group like %:word%") List<User> findByGroup(String word); }

O parâmetro word é capturado por padrão e mapeado para %:word%, esse tipo de implementação pode ser útil caso não queira criar métodos com nomes enormes que acabam mais atrapalhando que ajudando. Como sabemos, variáveis podem ser definidas no JPQL após ":" e caso o nome da variável seja diferente do parâmetro recebido no método, podemos fazer o bind usando @Param(X) em que X é o nome do parâmetro a ser substituído:

public interface UserRepository extends JpaRepository<User, Long> { @Query("from User where group = :word") List<User> findByGroup(@Param("word") String palavra); }

Criando nossas próprias implementações

É possível criar nossas próprias implementações dos métodos que vão interagir com o banco de dados além das que o Spring gera em tempo de execução. Para isso, devemos criar além da interface que estende de JpaRepository, uma interface com a assinatura dos novos métodos que vamos implementar e criar uma classe que receba o mesmo nome do repositório com a adição do sufixo Impl e que implemente essa interface com os novos métodos, é necessário também anotar a classe com @Repository para que o Spring gerencie em seu container:

public interface UserRepositoryQueries { List<User> listAllUsers(); }
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryQueries { }
@Repository public class UserRepositoryImpl implements UserRepositoryQueries { List<User> listAllUsers() { } }

Com isso feito, na nossa primeira interface UserRepository, vamos estender de UserRepositoryQueries e o Spring ficará responsável por mapear as implementações para a interface principal. Consultas e escrita no banco de dados podem ser feitas por meio do EntityManager, sendo isso uma implementação do JPA merece um post só para si, porém, precisamos dele para fazer nossas manipulações e podemos utilizar do sistema de injeção de dependências do Spring. Anotando uma propriedade do tipo EntityManager com @PersistenceContext o Spring vai injetar uma instância do EntityManager durante a inicialização da aplicação:

@Repository public class UserRepositoryImpl implements UserRepositoryQueries { @PersistenceContext private EntityManager manager; List<User> listAllUsers() { } }

A maioria dessas características são específicas do JPA e não dependem do Spring para funcionar e sim da implementação do Hibernate que pode ser usado isoladamente. No próximo post vamos falar sobre pool de conexões e HikariCP, a solução padrão do Spring Boot para essa estratégia!