Spring

Spring Data JPA

Se você está integrando sua aplicação a uma database baseado em ORM, então você deve conhecer sobre Spring Data JPA.

O Spring Data JPA adiciona uma camada sobre o JPA. Isso significa que ele usa todos os recursos definidos pela especificação JPA, especialmente o mapeamento de entidades e os recursos de persistência baseado em interfaces e anotações. Por isso, o Spring Data JPA adiciona seus próprios recursos, como uma implementação sem código do padrão de repositório e a criação de consultas de banco de dados a partir de nomes de métodos.

A partir de agora,nossa interação com o banco de dados será através de herança de interfaces e declaração de métodos com anotações.

Existem algumas interfaces e anotações que são super relevantes de explorar como:

Interfaces

  • CrudRepository
  • JPARepository
  • PagingAndSortingRepository

Anotações

  • @Query
  • @Param

Projeto Maven

Você pode utilizar o tutorial Primeiro Projeto ou criar um novo projeto conforme abaixo:

Criando um projeto pelo Spring Initialzr:

Acesse o site https://start.spring.io/ e coloque as informações com sugeridas abaixo:

Importe na IDE de sua preferência com um projeto Maven e aguarde realizar o download de todas as dependências necessárias.

Algumas IDEs como InteliJIdea, ao importar o projeto Maven, aparecerá uma ação de load na parte inferior à direita. Pode clicar para realizar o carregamento. Esta ação irá baixar e instalar as dependências. Assim como ao excluir ou adicionar novas depedências, o load vai aparecer novamente.

Mapeamento

Conheça as principais anotações do JPA.

Vamos imaginar que fomos designados a implementar um cadastro de usuário conforme diagrama abaixo:

Com os recursos do Spring Data JPA poderemos incluir, alterar, listar e excluir nossos usuários em uma base de dados.

Para isto ser possível, adicionaremos o starter spring-boot-starter-data-jpa no arquivo pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Mapeando a classe User.java

import javax.persistence.*;
import java.util.List;

@Entity
@Table(name = "tab_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_user")
    private Integer id;
    @Column(length = 50, nullable = false)
    private String name;
    @Column(length = 20, nullable = false)
    private String username;
    @Column(length = 100, nullable = false)
    private String password;
    @Transient
    private List<String> roles;

    public User(){

    }
    public User(String username){
        this.username = username;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public List<String> getRoles() {
        return roles;
    }
    public void setRoles(List<String> roles) {
        this.roles = roles;
    }
    public Integer getId() {
        return id;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", roles=" + roles +
                '}';
    }
}

Conhecendo as anotações

Todas as anotações utilizadas com a finalidade de mapeamento devem ser importados pelo pacote import javax.persistence.* .
  • @Entity: Torna a classe um entidade conectada a uma tabela no banco de dados
  • @Table: Necessário quando o nome da entidade difere do nome da tabela
  • @Id: Determina que o atributo representa a chave primária no banco de dados
  • @GeneratedValue: Determina a geração da chave primária
  • @Column: Necessário quando precisamos informar propriedade de definição DDL na entidade
  • @Transient: Quando o atributo não possui relação com nenhum campo no banco de dados

Primeira Persistência

Hora de persistir nossas entidades em um banco de dados.

Primeiro de tudo precisamos confirmar se nosso projeto contém o Start do spring-data-jpa.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Com o Spring Data JPA é possível conectar a qualquer banco de dados relacional, aqui está sendo utilizado o banco de dados H2, pois não precisa de configuração no application.properties.
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

Criando o repositório

import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Integer> {
    
}

Com o Spring Data JPA utilizamos interfaces para estender outras interfaces disponíveis pelo framework, precisando somente informar a classe e o tipo do campo @Id da entidade.

Primeiro CRUD

Crie a classe StartApplication implementando CommandLineRunner.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class StartApplication implements CommandLineRunner {
    @Autowired
    private UserRepository repository;
    @Override
    public void run(String... args) throws Exception {
        User user = new User();
        user.setName("GLEYSON");
        user.setUsername("glysns");
        user.setPassword("spring-data-jpa");
        user.setRoles(null);
        repository.save(user);

        for(User u: repository.findAll()){
            System.out.println(u);
        }
    }
}

Vamos esclarecer as principais linhas de código:

  • Na linha 8 estamos injetando o nosso UserRepository através na anotação @Autowired
  • Na linha 16 inserimos o usuário no banco de dados
  • Da linha 18 até 20 listamos os usuários existentes no banco de dados
Você pode habilitar tudo que acontece no banco adicionando a propriedade spring.jpa.show-sql=true no arquivos applications.properties

Execute sua aplicação a partir da sua classe que contém a anotação @SpringBootApplication.

Hibernate: insert into tab_user (id_user, name, password, username) values (null, ?, ?, ?)
Hibernate: select user0_.id_user as id_user1_0_, user0_.name as name2_0_, user0_.password as password3_0_, user0_.username as username4_0_ from tab_user user0_

User{name='GLEYSON', username='glysns', password='spring-data-jpa', roles=null}

Qualquer Bando de Dados

Conecte o seu projeto Spring Boot em qualquer banco de dados relacional.

O projeto Spring Data JPA é capaz de se conectar a qualquer banco de dados relacional.

Em um projeto Spring Boot toda a parte de configuração fica centralizada no arquivo application.properties inclusive configurações de banco. Vamos demonstrar uma configuração para acessar o banco de dados PostgreSQL mas serve para todos os bancos de dados relacionais.

# Opcional
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update

# Obrigatório de acordo com o seu banco de dados
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/seu_db
spring.datasource.username=seu_user
spring.datasource.password=seu_pass
  • A linha 2: Quando deseja exibir todo sql gerado no console.
  • A linha 3: O JPA é capaz de criar as tabelas do sistema conforme mapeamento.
  • A linha 6: Determina a plataforma de interpretação de SQL.
  • A linha 7: O drive do banco de dados.
  • A linha 8: A URL do banco de dados.
  • A linha 9: O usuário do banco de dados.
  • A linha 10: A senha do banco de dados.
Após definir qual banco de dados será utilizado, é necessário adicionar o drive como dependência.

Nova dependência no pom.xml

<!-- POSTGRES -->
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>

O Poderoso Repository

Explorando os principais recursos do JPA Repository.

Repository Pattern

Repository é um padrão de projeto similar ao DAO (Data Access Object) no sentido de que seu objetivo é abstrair o acesso a dados de forma genérica a partir do seu modelo.

Spring Data JPA

O projeto Spring Data JPA facilita a implementação do padrão Repository através de AOP (Aspect Oriented Programming - programação orientada a aspectos).

Utilizando-se apenas de uma interface, o Spring irá "gerar" dinamicamente a implementação dos métodos de acesso a dados. Estender a interface JpaRepository é opcional, mas a vantagem é que ela já vem com vários de métodos genéricos de CRUD e você não precisa redefinir todos eles.

O repositório seria uma classe para buscar informações no banco de dados ou no local onde as informações foram persistidas. Mas no caso do JpaRepository ele provê a ligação a determinada classe do Model com possibilidade de persistir no banco de dados. https://pt.stackoverflow.com/questions/4088/utilizando-o-reposit%C3%B3rio-do-jpa

Principais métodos que já são disponibilizados pelo framework:

  • save: Insere e atualiza os dados de uma entidade.
  • findById: Retorna o objeto localizado pelo seu ID.
  • existsById: Verifique a existência de um objeto pelo ID informado, retornando o boolean.
  • findAll: Retorna uma coleção contendo todos os registros da tabela no banco de dados.
  • delete: Deleta um registro da respectiva tabela mapeada do banco de dados.
  • count: retorna a quantidade de registros de uma tabela mapeada no banco de dados.
E a implementação? O Spring Data JPA se encarrega de realizar a implementação através do padrão CRUD.

Consultas Customizadas

Existem duas maneiras de realizar consultas customizadas, uma é conhecida como QueryMethod e a outra é QueryOverride.

Query Method

O Spring Data JPA se encarrega de interpretar a assinatura de um método (nome + parâmetros) para montar a JPQL correspondente.

Veja o exemplo de uma entidade que possui um endereço de e-mail e sobrenome e gostaria de filtrar por estes dois atributos.

public interface UserRepository extends Repository<User, Long> {

  List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);

}

Conforme a documentação oficial do Spring Data JPA, abaixo estão algumas instruções possíveis para montar JPQLs via métodos.

Query Override

Vamos imaginar que você precisará montar uma query um tanto avançada mas ficaria inviável utilizar o padrão QueryMethod? Como nossos repositórios são interfaces não temos implementação de código, é ai que precisa definir a consulta de forma manual através da anotação @Query.

Os dois métodos realizam a mesma instrução SQL, consultando os usuários pelo seu campo name comparando com o perador LIKE do SQL.

public interface UserRepository extends JpaRepository<User, Integer> {
    //Query Method - Retorna a lista de usuários contendo a parte do name
    List<User> findByNameContaining(String name);
    
    //Query Override - Retorna a lista de usuários contendo a parte do name
    @Query("SELECT u FROM User u WHERE u.name LIKE %:name%")
    List<User> filtrarPorNome(@Param("name") String name);
    
    //Query Method - Retorna um usuário pelo campo username
    User findByUsername(String username);

}