Tutorial: Quarkus — Simplificando o Hibernate utilizando Panache — criando uma aplicação simples utilizando Quarkus Java + REST + CDI + Panache
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.
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:
- Você vai precisar de uma IDE como por exemplo: IntelliJ IDEA, Eclipse, VSCode.
- Instale a JDK 8 or 11+
- Instale o Apache Maven 3.5.3+ ou o Gradle
- Panache Entity
- Docker
- Escolha um cliente para conectar com o Banco de dados, exemplo: DBeaver, MySql Workbench, entre outros.
- Cliente para realizar requisições REST: Postman ou o Insomnia.
Instruções Adicionais:
- Instalação do Docker (Documentação oficial)
- Instalando Docker no windows: (Youtube, ESR)
- Instalando o Docker no Linux: (Youtube: LinuxTips)
- Instalando o Docker no Mac: (Youtube: Wellington Rogati)
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;
// 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();
}
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();
}
// 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();
}
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)