DEV Community

Cover image for Simplificando o Hibernate utilizando Panache - criando uma aplicação simples utilizando Quarkus Java + REST + CDI + Panache
Marcus Paulo
Marcus Paulo

Posted on

Simplificando o Hibernate utilizando Panache - criando uma aplicação simples utilizando Quarkus Java + REST + CDI + Panache

Tutorial: Quarkus — Simplificando o Hibernate utilizando Panache — criando uma aplicação simples utilizando Quarkus Java + REST + CDI + Panache

Logo — Tartarugas e um Jacaré

Se inscreva no meu canal do Youtube, vários conteúdos para Desenvolvedores: https://www.youtube.com/channel/UCzoyY_hIVs53dQUeY4yVkhA

Introdução

Este artigo tem o objetivo de apresentar uma alternativa para simplificar a implementação da camada de persistência em uma aplicação Java. A maioria dos desenvolvedores Java conhecem a especificação JPA (Java Persistence API), que é uma interface comum para frameworks que trabalham com persistência de dados. Uma das implementações mais conhecidas é o Hibernate.

O Hibernate é um framework ORM (Mapeamento Objeto Relacional), que tem como objetivo, facilitar o mapeamento das tabelas, atributos e relacionamentos de um banco de dados para uma classe java.

No exemplo abaixo, podemos ver uma das formas para implementação de uma consulta para obter todos os carros de uma base de dados. No primeiro trecho de código, observamos a criação do EntityManagerFactory e em seguida a criação de um EntityManager, por fim, a consulta é criada retornando a lista de carros.

Implementação para retornar a lista de carros

Como forma de comparação, veja no exemplo abaixo, a mesma implementação da recuperação da lista de todos os carros, utilizando o Panache.

O objetivo do Panache é simplificar a camada de persistência, principalmente das operações triviais.

No exemplo abaixo, podemos ver uma implementação para salvar um objeto carro no Banco de Dados. Explicando o código: O objeto carro é criado e “populado”, em seguida, ocorre a criação do EntityManagerFactory e do EntityManager. Antes de salvar o objeto é necessário abrir a conexão, salvar o carro e em seguida realizar o commit. Antes de terminar a execução do método é necessário fechar a conexão do banco de dados, através da instrução close();

* Exemplo adaptado da apostila da Caelum: https://www.caelum.com.br/apostila-java-web/uma-introducao-pratica-ao-jpa-com-hibernate/#trabalhando-com-os-objetos-o-entitymanager

No código-fonte abaixo, segue a implementação do salvar, utilizando o Panache.

Para utilizar os benefícios do Panache é necessário que a entidade estenda a classe PanacheBaseEntity. Dessa forma, a Classe que herda terá diversos métodos prontos para serem utilizados.

Voltando para o código, na primeira parte do código o Carro é instanciado e “populado”, em seguida, invoca o método estático (persist) da Classe Carro para salvar o objeto no banco de dados, para finalizar devolve o objeto para a requisição REST.

O Panache é responsável por gerenciar toda a transação, desde da abertura, commit e o fechamento da transação.

A ideia é facilitar a implementação, melhorando assim a produtividade, já que o código fica bem mais simples, se comparado a forma tradicional.

Métodos da classe PanacheEntityBase

Ao estender a classe PanacheEntityBase, a classe herda vários métodos úteis, como: Listar todos, salvar, atualizar, remover, buscar um objeto, retornar a quantidade de objetos de uma determinada tabela.

Listando carros com o nome Fusca, basta escrever: Car.find(“name”, “fusca”);

Listando carros ordenados por nome, basta escrever: Car.find("order by name”)

Métodos da classe PanacheEntityBase, para mais detalhes, veja o código-fonte

Abaixo a lista de métodos que são herdados, ao estender a classe PanacheEntityBase.

  • persist — Persiste uma entidade no banco de dados.
  • flush — Realiza o commit ou rollback de todas as transações pendentes
  • findById — Busca uma entidade por meio do ID
  • find — Lista os registros de uma entidade. (Parâmetros de Filtros e Sort são opcionais)
  • findAll — Lista todos os registros de uma entidade.
  • list — Atalho para find().list()
  • listAll — Atalho para findAll().list()
  • Count — Exibe a quantidade de registros de uma entidade. Você pode passar um parâmetro que é opcional.
  • deleteAll — Remover uma lista de objetos do banco de dados.
  • delete — Remove uma entidade, podendo utilizar uma Query (Opcional)

Parte prática — Hands-on (Tutorial passo a passo)

Para facilitar o entendimento do Framework Panache, será criado um projeto em Quarkus + Panache e um banco de dados em Postgres, mostrando os diversos cenários em que o Panache pode auxiliar o desenvolvedor.

Para mais detalhes do Quarkus, veja o meu artigo: https://www.linkedin.com/pulse/tutorial-criando-um-crud-utilizando-quarkus-java-rest-da-silva-melo/

Antes de iniciar o desenvolvimento da aplicação, é necessário atender os requisitos mínimos abaixo:

Requisitos mínimos:

Instruções Adicionais:

Escopo da aplicação

A partir de agora, vamos criar uma aplicação que será desenvolvida com o Quarkus, utilizando o Panache + Hibernate para persistência, CDI e JAX-RS para a API REST.

A aplicação consiste em realizar Cadastro, Alteração, Atualização, Listagem, Paginação, Consultas Personalizadas e entre outras funcionalidades do Panache para gerenciar os Carros cadastrados no banco de dados.

// Criação do projeto em Quarkus

mvn io.quarkus:quarkus-maven-plugin:1.0.1.Final:create \

-DprojectGroupId=br.com.car \

-DprojectArtifactId=quarkus-panache-car \

-DclassName="br.com.car.resource.CarResource" \

-Dpath="/cars"

Em seguida, vamos executar o projeto, através do comando:

mvn compile quarkus:dev

No navegador, acesse o endereço: http://localhost:8080

Configurando o Panache no Projeto

Adiciona as configurações abaixo no arquivo application.properties, na pasta resource na raiz do projeto.

configure your datasource

quarkus.datasource.url = jdbc:postgresql://localhost:5433/quarkus-panache-car

quarkus.datasource.driver = org.postgresql.Driver

quarkus.datasource.username = postgres

quarkus.datasource.password = postgres

drop and create the database at startup (use update to only update the schema)

quarkus.hibernate-orm.database.generation = drop-and-create

Adicione as dependências do Panache no pom.xml.



io.quarkus

quarkus-hibernate-orm-panache



io.quarkus

quarkus-jdbc-postgresql



io.quarkus

quarkus-resteasy-jsonb

Observação: Para criar a massa de dados no início da aplicação, basta adicionar o arquivo import.sql, dentro da pasta resource do projeto.

Executando uma instância do Postgresql no Docker

Para armazenar as informações, iniciaremos uma instância do Postgresql, utilizando o Docker, para isso, é necessário ter o Docker instalado e então executar o comando listado abaixo:

Criando uma instância do Postgressql através do Docker

docker run --name postgres-car -e "POSTGRES_PASSWORD=postgres" -p 5433:5432 -v ~/developer/PostgreSQL:/var/lib/postgresql/data -d postgres

Entidade Car (o modelo da aplicação)

@Entity

public class Car extends PanacheEntityBase {

@Id  
@GeneratedValue(strategy = GenerationType.IDENTITY)  
private Long id;

private String brand;

private String name;

@Column(name="model_year")  
private LocalDate modelYear;

@Column(name="created_date_time")  
private LocalDateTime createdDateTime;

@Column(name="is_available_sale")  
private Boolean isAvailableSale;

private BigDecimal price;
Enter fullscreen mode Exit fullscreen mode

// Omitido

Observação: O atributo Id (Chave Primária) é opcional, quando a classe estende o PanacheEntityBase

Testando a aplicação

Para fins didáticos, vamos separa os testes em duas partes, a primeira será utilizando os métodos do PanacheEntityBase, dentro da classe Resource (EndPoint). A segunda parte, será utilizando uma classe Repository + Resource(EndPoint).

Observação 1: Outra forma seria criar uma camada de Serviço (Service) para “chamar” os métodos de uma classe DAO ou de um Repositório.

Observação 2: Sinta-se a vontade para definir o melhor padrão de sufixos de nomes, exemplo: Camada do EndPoint (API/Rest): (Controller, EndPoint, …). Camada de Negócio: (Controller, Manager, Business, ….) e assim por diante.

Listando todos os Carros (Versão 1)

Para acessar a lista de todos os carros, basta chamar o método estático da classe Car, que é herdado a partir do PanacheEntityBase.

@path("/cars")

@Produces(MediaType.APPLICATION_JSON)

public class CarResource {

@GET  
public List<Car> listAll() {  
    return Car.listAll();

}
Enter fullscreen mode Exit fullscreen mode

Resultado no Postman

Outro forma é implementar na classe de sufixo Repository ou na classe DAO. A implementação será utilizando a classe DAO ou Repository, bastando implementar PanacheRepository, conforme o exemplo abaixo:

@ApplicationScoped

public class CarRepository implements PanacheRepository {

public List<Car> listAll() {  
    return listAll();  
}
Enter fullscreen mode Exit fullscreen mode

// omitido

A implementação na classe CarV2Resource (EndPoint Rest):

@path("/cars/v2")

@Produces(MediaType.APPLICATION_JSON)

public class CarV2Resource {

@Inject

private CarRepository carRepository;

@GET

public List<Car> listAll() {

return carRepository.listAll();

}

Enter fullscreen mode Exit fullscreen mode




Postman — Listar todos os veículos na versão 2 da API

O resultado é exatamente igual, quando implementado diretamente na classe do Endpoint da API.

Criando um veículo (Versão 1)

Criando um veículo, invocando o método estático da Classe Car, dentro da CarResource(EndPoint), conforme exemplo abaixo:

A anotação @Transactional é responsável por gerenciar a transação efetuando um commit ou um rollback na transação, caso seja necessário;

@post

@Transactional

public Response create(Car car) {

Car.persist(car);

return Response.ok(car).status(Response.Status.CREATED).build();

}

Evoluindo a criação de um veículo

No endpoint CarV2Resource, basta chamar o método save, que está implementado no PanacheRepository.

Classe: CarV2Resource.java

@post

public Response create(Car car) {

Car carEntity = carRepository.save(car);

return Response.ok(car).status(Response.Status.CREATED).build();

}

Postman — Resultado da inclusão de um veículo

Listando os carros por ano

// CarResource.java

@GET

@path("/listCarsByYear")

public List listCarsByYear(@QueryParam("year") int year) {

return Car.find("year(modelYear) = :year", Parameters.with("year",

year)).list();

}

Na versão 2, a implementação utiliza o carRepository e chama o método listCarsByYear(year);

// CarV2Resource.java

@GET

@path("/listCarsByYear")

public List listCarsByYear(@QueryParam("year") int year) {

return carRepository.listCarsByYear(year);

}

Implementação da lógica de pesquisa de carro por ano, na classe CarRepository.java

//CarRepository.java

public List listCarsByYear(int year) {

return find("year(modelYear) = :year", Parameters.with("year", year)).list();

}

Postman — Resultado da busca de veículos por ano

Outros métodos:

Listar todos os carros que estão disponíveis para venda

Listar todos os veículos ordenados por Nome e Marca.

Listar todos os veículos passando o ano como Parâmetro

Retornar a quantidade de veículos cadastrados

Listar veículos com paginação

Atualizar veículo

Conclusão

Espero que vocês tenham gostado, sinta-se à vontade para sugerir, criticar ou elogiar. Um grande abraço e até a próxima.

Código-Fonte

O código-fonte está disponível no endereço: https://github.com/marcuspaulo/quarkus-panache-car

Top comments (0)