DEV Community

Marcus Paulo
Marcus Paulo

Posted on

Tutorial: Criando um CRUD utilizando Quarkus Java + REST + CDI + Panache, Hibernate com Postgres (Docker) + Postman

Tutorial: Criando um CRUD utilizando Quarkus Java + REST + CDI + Panache, Hibernate com Postgres (Docker) + Postman

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

Introdução

Os programadores de Java estão “cansados” de ouvir as seguintes expressões: “Java é lento!”, “O pacote do java é grande”, “O tempo de inicialização é lento!”. Como uma forma de resolver isso, a Red Hat lançou o Quarkus, um framework Java nativo do Kubernetes feito sob medida para o GraalVM e OpenJDK HotSpot. O Quarkus visa tornar o java uma plataforma líder em ambientes serverless e Kubernetes, oferecendo aos desenvolvedores um modelo unificado de programação reativa e imperativa.

O QuarkusIO, promete entregar pacotes menores, com um tempo de inicialização extremanente rápido, menor consumo de memória, desde que combinado com a GraalVM, O Quarkus vai realizar a compilação Ahead-of-time(AOT). Baseada nos melhores padrões, plataforma integrada, baixo consumo de processamento e tempo de inicialização é incrivelmente rápido, em milisegundos, Quarkus permite o uso do Java em ambientes serverless, suportando um ambiente responsivo e escalável.

Na imagem abaixo, podemos ver a comparação do consumo e memória e o tempo de inicialização + tempo da resposta da primeira requisição, podemos perceber que o Quarkus + OpenJDK + GraalVM, consomem menos memória e iniciam mais rapidamente, do que a o Quarkus + OpenJDK e um framework equivalente de mercado.

Um grande diferencial do Quarkus é que ele suporta as principais especificações, ou seja, você, não precisa aprender nada de novo. Por exemplo, você pode utilizar o CDI e JAX-RS, também é possível suportar as extensões como: Hibernate, Kafka, OpenShift, Kubernetes. Na era do cloud, no qual containers, Kubernetes, microservices, functions-as-a-service (Faas), e aplicações nativas para o cloud estão apresentando altos níveis de produtividade e eficiência, o Quarkus surge com uma alternativa muito interessante.

Antes de continuar o post, gostaria de compartilhar a apresentação que fiz nesse ano.

Apresentação no Evento Brasília Dev Festival 2019

Em Setembro de 2019, palestrei no evento Brasília Dev Festival, realizado em Brasília, foi um evento recheado de muito conhecimento e network, a apresentação está disponível abaixo:

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

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, vamos utilizar o CDI para Injeção de Dependência e JAX-RS para a API REST.

Vamos criar uma API para Listar, Cadastrar, Editar e Excluir alimentos.

O Quarkus possui um Archetype para criação da aplicação de forma fácil, basta executar o comando abaixo:

  • Sugestão: verifique a última versão no site oficial do Quarkus.io.

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

-DprojectGroupId=br.com.food \

-DprojectArtifactId=quarkus-food \

-DclassName="br.com.food.resource.FoodResource" \

-Dpath="/food"

$ cd quarkus-food

Vamos entender o que cada trecho significa:

Plugin do Maven para criação do Projeto no Quarkus

$ mvn io.quarkus:quarkus-maven-plugin:1.11.1.Final:create

Definição do pacote do projeto

-DprojectGroupId=br.com.food

Definição do Nome do Projeto

-DprojectArtifactId=quarkus-food

Definindo o caminho da classe Rest (inicial)

-DclassName="br.com.food.resource.FoodResource"

Definição da URI, final do endereço para acessar no navegador

-Dpath="/food

O Quarkus disponibiliza um site chamado Quarkus.code.io, onde é posísvel configurar o projeto de uma forma mais visual, vale a pena conferir, segue o link: https://code.quarkus.io/

Site: code.quarkus.io

Criando o projeto — Executando o comando de criação

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

-DprojectGroupId=br.com.mp \  
-DprojectArtifactId=food \  
-DclassName="br.com.mp.FoodResource" \  
-Dpath="/food"

// Log omitido

[INFO] Your application will be accessible on http://localhost:8080

[INFO] ------------------------------------------------------------------------

[INFO] BUILD SUCCESS

[INFO] ------------------------------------------------------------------------

[INFO] Total time: 42.305 s

[INFO] Finished at: 2019-12-12T09:36:11-02:00

[INFO] ------------------------------------------------------------------------

Log completo você encontra aqui: https://pastebin.com/UfczCUDL

Observação: A criação do projeto pode levar um tempo, já que o Maven, irá realizar o download de todas as dependências

Abra a aplicação em sua IDE preferida

Para esse exemplo, estou utilizando o InteliJ, porém, gosto bastante do Eclipse e Visual code, sinta-se a vontade para escolher sua IDE favorita.

Na imagem abaixo, podemos ver a estrutura do projeto (Maven), ressaltando a criação dos arquivos dentro da pasta Docker, Dockerfile.jvm e Dockerfile.native, que pretendo falar em um outro artigo. Logo abaixo, veremos o arquivo FoodResource, essa classe é a responsável por “expor” a API.

Executando o projeto inicial

No terminal, execute o comando abaixo:

mvn compile quarkus:dev

Resultado da execução:

Possivelmente, na primeira execução o compilador irá verificar se existe alguma dependência para baixar, como no exemplo abaixo.

[INFO] Scanning for projects...

[INFO]

[INFO] ---------------------------< br.com.mp:food >---------------------------

[INFO] Building food 1.0-SNAPSHOT

[INFO] --------------------------------[ jar ]---------------------------------

[INFO]

[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ food ---

[INFO] Using 'UTF-8' encoding to copy filtered resources.

[INFO] Copying 2 resources

// LOG OMITIDO

[INFO] ---------------------------< br.com.mp:food >---------------------------

[INFO] Building food 1.0-SNAPSHOT

[INFO] --------------------------------[ jar ]---------------------------------

[INFO]

[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ food ---

2019-12-12 15:12:19,730 INFO io.quarkus Quarkus 1.0.1.Final started in 0.735s. Listening on: http://0.0.0.0:8080

2019-12-12 15:12:19,740 INFO io.quarkus Profile dev activated. Live Coding activated.

2019-12-12 15:12:19,741 INFO io.quarkus Installed features: [cdi, resteasy]

Log completo da execução da aplicação: https://pastebin.com/pnLdVYsc

No final Log, aparece a URL para acessar com o navegador.

io.quarkus Quarkus 1.0.1.Final started in 0.735s. Listening on: http://0.0.0.0:8080

Você pode acessar a API através dos endereços: http://localhost:8080, http://0.0.0.0:8080 ou http://127.0.0.1:8080.

Para acessar a página, basta clicar no endereço exposto no terminal ou simplesmente copiar e colar no seu navegador, como mostrado no exemplo abaixo:

Testando a API /food, através do navegador

A URI da nossa API está definida inicialmente na classe FoodResource.java, com o caminho : /food

Abra o navegador e digite o endereço: http://localhost:8080/food, conforme a imagem a seguir:

Executando uma instância do Postgresql no Docker

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

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

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

Observação: Precisei mudar a porta padrão de 5432 para 5433, já que tinha uma outra instância rodando aqui.

Vamos testar a conexão com o Banco de dados, no meu caso, eu estou utilizando o DBeaver 6.1.0, porém, existem diversas outras aplicações para conectar com o Banco de Dados PostgreSQL, abaixo, segue o teste da conexão do banco de dados:

No cliente SQL, vamos abrir o schema do Banco de dados e percebemos que o mesmo está vazio, não existe nenhuma tabela criada.

Voltando para a nossa aplicação

Retorne para sua IDE e adicione as seguintes dependências para o PanacheEntity e a dependência do Postgresql, as informações devem ser adicionadas no arquivo pom.xml (Na raiz do projeto).



io.quarkus

quarkus-hibernate-orm-panache



io.quarkus

quarkus-jdbc-postgresql



io.quarkus

quarkus-resteasy-jsonb

O Panache Entity visa facilitar a implementação da camada de persistência da aplicação. Além disso, o framework trás diversos métodos como Count, ListAll(), findById, persist, delete já implementados, conforme exibido código abaixo

Exemplo do PanacheEntity (Retirado do site: https://quarkus.io/guides/hibernate-orm-panache)

// creating a person

Person person = new Person();

person.name = "Stef";

person.birth = LocalDate.of(1910, Month.FEBRUARY, 1);

person.status = Status.Alive;

// persist it

person.persist();

// note that once persisted, you don't need to explicitly save your entity: all

// modifications are automatically persisted on transaction commit.

// check if it's persistent

if(person.isPersistent()){

// delete it

person.delete();

}

// getting a list of all Person entities

List allPersons = Person.listAll();

// finding a specific person by ID

person = Person.findById(personId);

// finding all living persons

List livingPersons = Person.list("status", Status.Alive);

// counting all persons

long countAll = Person.count();

// counting all living persons

long countAlive = Person.count("status", Status.Alive);

// delete all living persons

Person.delete("status", Status.Alive);

// delete all persons

Person.deleteAll();

Observação: O PanacheEntity é muito interessante, porém, vou explicar melhor em um artigo futuro.

Voltando para a implementação da classe de modelo (Entity)

Na IDE, crie a classe (Entidade): Food.java, dentro da pasta br.com.food.entity.

Classe: Food.java

package br.com.food.entity;

import io.quarkus.hibernate.orm.panache.PanacheEntityBase;

import javax.persistence.*;

import java.util.Objects;

@Entity

public class Food extends PanacheEntityBase {

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

@Column  
private String name;  

@Column  
private Double calories;
Enter fullscreen mode Exit fullscreen mode

// Omitido

}

Configurando a conexão de Banco de dados no projeto

Procure o arquivo application.properties, dentro da pasta resources e adicione as seguintes configurações:

configure your datasource

quarkus.datasource.url = jdbc:postgresql://localhost:5433/food

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

Ao executar nossa aplicação, nos deparamos com o seguinte problema: Caused by: org.postgresql.util.PSQLException: FATAL: database “food” does not exist

Caused by: org.postgresql.util.PSQLException: FATAL: database "food" does not exist

at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2468)

at org.postgresql.core.v3.QueryExecutorImpl.readStartupMessages(QueryExecutorImpl.java:2587)

at org.postgresql.core.v3.QueryExecutorImpl.(QueryExecutorImpl.java:134)

at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:250)

at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)

at org.postgresql.jdbc.PgConnection.(PgConnection.java:195)

at org.postgresql.Driver.makeConnection(Driver.java:458)

at org.postgresql.Driver.connect(Driver.java:260)

Solução: Criar um Database no Postgresql, através do ClienteSQL

Executando a aplicação com o Banco de Dados configurado

Retornando para a nossa IDE, vamos executar o comando que irá iniciar nossa aplicação:

$ mvn compile quarkus:dev

[INFO] Scanning for projects...

[INFO]

[INFO] --------------------------< br.com.food:food >--------------------------

[INFO] Building food 1.0-SNAPSHOT

[INFO] --------------------------------[ jar ]---------------------------------

[INFO]

[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ food ---

[INFO] Using 'UTF-8' encoding to copy filtered resources.

[INFO] Copying 2 resources

[INFO]

[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ food ---

[INFO] Changes detected - recompiling the module!

[INFO] Compiling 2 source files to /home/marcus/Estudo/Quarkus/food/target/classes

[INFO]

[INFO] --- quarkus-maven-plugin:1.0.1.Final:dev (default-cli) @ food ---

Listening for transport dt_socket at address: 5005

2019-12-12 16:44:24,885 WARN org.hib.eng.jdb.spi.SqlExceptionHelper SQL Warning Code: 0, SQLState: 00000

2019-12-12 16:44:24,891 WARN org.hib.eng.jdb.spi.SqlExceptionHelper table "food" does not exist, skipping

2019-12-12 16:44:25,135 INFO io.quarkus Quarkus 1.0.1.Final started in 1.715s. Listening on: http://0.0.0.0:8080

2019-12-12 16:44:25,136 INFO io.quarkus Profile dev activated. Live Coding activated.

2019-12-12 16:44:25,136 INFO io.quarkus Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, jdbc-postgresql, narayana-jta, resteasy]

Obversação 1: Assim que finalizar a execução, percebemos que a tabela Food foi criada no banco de dados.

Obvervação 2: Caso não queira o comportamento de criação automatica do banco de dados, basta comentar ou remover a linha: “quarkus.hibernate-orm.database.generation = drop-and-create”, de dentro do arquivo do application.properties.

Verificando se a tabela foi criada no Banco de Dados.

Espero que esteja gostando desse artigo.

Implementando uma simples classe de negócio

Crie uma classe chamada de FoodController.java, dentro da pasta br.com.food.controller. Essa classe será responsável por conter uma regra de negócio simples.

@ApplicationScoped

public class FoodController {

public Food update(Long id, Food food) {  
    Food foodEntity = Food.findById(id);

    if (foodEntity == null) {  
        throw new WebApplicationException("Food with id of " + id + " does not exist.", Response.Status.NOT_FOUND);  
    }

    foodEntity.setName(food.getName());  
    foodEntity.setCalories(food.getCalories());

    return foodEntity;  
}

/**  
 * This method is main purpose to show simple "Business" example  
 * @param food  
 * @return  
 */  
public boolean isFoodNameIsNotEmpty(Food food) {  
    return food.getName().isEmpty();  
Enter fullscreen mode Exit fullscreen mode

}

}

Implementação do FoodResouce.java

A classe FoodResource, é responsável por expor a API com os métodos para cadastro, edição, remoção e lista de todos os alimentos cadastrados.

FoodResource.java

package br.com.food.resource;

// OMITIDO

@Path("/food")

@Produces(MediaType.APPLICATION_JSON)

@Consumes(MediaType.APPLICATION_JSON)

public class FoodResource {

@Inject  
private FoodController foodController;

@GET  
public List<Food> findAll() {  
    return Food.listAll();  
}

@POST  
@Transactional  
public Response create(Food food) {  
    Food.persist(food);  
    return Response.ok(food).status(201).build();  
}

@PUT  
@Path("{id}")  
@Transactional  
public Response update(@PathParam("id") Long id, Food food) {

    if (foodController.isFoodNameIsNotEmpty(food)) {  
        return Response.ok("Food was not found").type(MediaType.APPLICATION_JSON_TYPE).build();  
    }

    Food foodEntity = foodController.update(id, food);

    return Response.ok(foodEntity).build();  
}

@DELETE  
@Path("{id}")  
@Transactional  
public Response delete(@PathParam("id") Long id) {  
    Food foodEntity = Food.findById(id);

    if (foodEntity == null) {  
        throw new WebApplicationException("Food with id " + id + " does not exist.", Response.Status.NOT_FOUND);  
    }

    foodEntity.delete();  
    return Response.status(204).build();  
}  
Enter fullscreen mode Exit fullscreen mode

}

Observação: Reparem que é possível efetuar uma operação de persistência tanto na camada de Resource(API), quanto na classe de negócio ou até mesmo em uma classe Data Access Object — DAO.

Testando a API através do Postman

Abra o Postman, selecione o método POST e digite a URL: http://localhost:8080/food. Adicionalmente, adicione um Body com os dados conforme a imagem abaixo:

Cadastrando outro alimento no Postman

Listando todas os alimentos no Postman

No Postman, crie uma outra requisição do tipo GET e digite a url: http://localhost:8080/food, o resultado será a lista de todos os alimentos cadastrados no banco de dados, conforme a imagem abaixo:

Postman — Atualizando um objeto

Para atualizar a descrição e/ou a caloria de um alimento, basta criar uma nova requisição do tipo PUT, adicionar no final da URL o código (ID) do alimento que modificado. Não esqueça de preencher o body, que contém o JSON do objeto alterado.

Verificando os registros no Banco de Dados

Removendo um alimento através do Postman

Listando todos os alimentos através do Postman

No último teste, será listado todos os alimentos que estão cadastrados no banco de dados.

Conclusão

Esse é o meu primeiro artigo e 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-food

Top comments (0)