Como criar um aplicativo CRUD com MongoDB, Quarkus e GraalVM
Maxime Beugnet7 min read • Published Aug 29, 2024 • Updated Aug 29, 2024
APLICATIVO COMPLETO
Avalie esse Início rápido
Quando escrevemos um aplicativo Java tradicional, nosso código-fonte Java é compilado e transformado em bytecode Java. Esse bytecode pode ser executado por uma Java virtual machine (JVM) específica para o sistema operacional que você está executando. É por isso que podemos dizer que Java é uma linguagem portátil. Você compila uma vez e pode executá-lo em qualquer lugar, desde que tenha a JVM certa na máquina certa.
Este é um grande mecanismo, mas tem um custo. Iniciar um programa é lento porque o JVM e todo o contexto precisam ser carregados primeiro antes de executar qualquer coisa. Não é eficiente em termos de memória porque precisamos carregar centenas de classes que podem não ser usadas, pois a verificação do caminho de classeocorre somente depois de.
Isso funcionava perfeitamente no antigo reino monolítico, mas é totalmente inaceitável no novo mundo feito de Lambda, cloud, container e Kubernetes. Nesse contexto, um baixo consumo de memória e um tempo de inicialização extremamente rápido são absolutamente obrigatórios.
É aqui que entra oBloco . O Bloco é um framework Java nativo doKubernetes personalizado para GraalVM e hotSpot.
Com o Quarkus, você pode construir binários nativos que podem inicializar e enviar sua primeira resposta no 0.042 segundos versus 9.5 segundos para um aplicativo Java tradicional.
Neste tutorial, construiremos um aplicativo do Quartokus que pode gerenciar uma collection do
persons
no MongoDB. O objetivo é executar quatro operações CRUD simples com uma REST API usando um aplicativo nativo.Para este tutorial, você precisará de:
Se você não quiser codificar junto e preferir conferir diretamente o código final:
1 git clone git@github.com:mongodb-developer/quarkus-mongodb-crud.git
A maneira mais fácil de colocar seu projeto em funcionamento com o Quarkus e todas as dependências necessárias é usar https://code.4kus.io/.
Semelhante ao Spring initializr, o site inicial do projeto Quartokus ajudará você a selecionar suas dependências e construir seu arquivo de configuração Maven ou Gradle. Algumas dependências também incluirão um código inicial para ajudá-lo em seus primeiros passos.
Para o nosso projeto, vamos precisar de:
- Cliente MongoDB [quárkus-mongodb-cliente].
- SmallRYE OpenAPI [quakus-smallrye-openapi].
- REST [quarkus-rest].
- REST dentro de jackson.
Sinta-se livre para usar o
group
e oartifact
de sua escolha. Certifique-se de que a versão do Java corresponda à versão da sua versão do GraalVM e estamos prontos para Go.Baixe o arquivo zip e descompacte-o na pasta de projeto de sua preferência. Quando terminar, dedique algum tempo para ler o arquivo README.md fornecido.
Por fim, precisamos de um cluster MongoDB. Duas soluções:
- Crie um conjunto de réplicas de nó único efêmero com o Docker.
1 docker run --rm -d -p 27017:27017 -h $(hostname) --name mongo mongo:latest --replSet=RS && sleep 5 && docker exec mongo mongosh --quiet --eval "rs.initiate();"
De qualquer forma, a próxima etapa é configurar sua connection string no arquivo
application.properties
.1 quarkus.mongodb.connection-string=mongodb://localhost:27017
Agora que nosso projeto Quakus está pronto, podemos começar a desenvolver.
Primeiro, podemos iniciar o modo de desenvolvedor, que inclui codificação ao vivo (atualização automática) sem a necessidade de reiniciar o programa.
1 ./mvnw compile quarkus:dev
O modo de desenvolvedor vem com dois recursos úteis:
Sinta-se à vontade para explorar essas duas UIs e ver os recursos que elas oferecem.
Além disso, como seu serviço agora está em execução, você deverá receber sua primeira comunicação HTTP. Abra um novo terminal e execute a seguinte consulta:
1 curl http://localhost:8080/hello
Nota: se você clonou o repositório, então é
/api/hello
. Estamos mudando isso abaixo em um minuto.Resultado:
1 Hello from Quarkus REST
Isso funciona porque seu projeto atualmente contém uma única classe
GreetingResource.java
com o código a seguir.1 package com.mongodb; 2 3 import jakarta.ws.rs.GET; 4 import jakarta.ws.rs.Path; 5 import jakarta.ws.rs.Produces; 6 import jakarta.ws.rs.core.MediaType; 7 8 9 public class GreetingResource { 10 11 12 13 public String hello() { 14 return "Hello from Quarkus REST"; 15 } 16 }
"Olá do Quartokus REST" é bom, mas não é o nosso objetivo! Queremos manipular dados de uma coleção
persons
no MongoDB.Vamos criar um
PersonEntity.java
clássico classePOJO. Criei-o no pacote com.mongodb
padrão, que é o meugroup
anterior. Sinta-se livre para alterá-lo.1 package com.mongodb; 2 3 import com.fasterxml.jackson.databind.annotation.JsonSerialize; 4 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; 5 import org.bson.types.ObjectId; 6 7 import java.util.Objects; 8 9 public class PersonEntity { 10 11 12 public ObjectId id; 13 public String name; 14 public Integer age; 15 16 public PersonEntity() { 17 } 18 19 public PersonEntity(ObjectId id, String name, Integer age) { 20 this.id = id; 21 this.name = name; 22 this.age = age; 23 } 24 25 26 public int hashCode() { 27 int result = id != null ? id.hashCode() : 0; 28 result = 31 * result + (name != null ? name.hashCode() : 0); 29 result = 31 * result + (age != null ? age.hashCode() : 0); 30 return result; 31 } 32 33 34 public boolean equals(Object o) { 35 if (this == o) return true; 36 if (o == null || getClass() != o.getClass()) return false; 37 38 PersonEntity that = (PersonEntity) o; 39 40 if (!Objects.equals(id, that.id)) return false; 41 if (!Objects.equals(name, that.name)) return false; 42 return Objects.equals(age, that.age); 43 } 44 45 public ObjectId getId() { 46 return id; 47 } 48 49 public void setId(ObjectId id) { 50 this.id = id; 51 } 52 53 public String getName() { 54 return name; 55 } 56 57 public void setName(String name) { 58 this.name = name; 59 } 60 61 public Integer getAge() { 62 return age; 63 } 64 65 public void setAge(Integer age) { 66 this.age = age; 67 } 68 }
Agora que temos um
PersonEntity
, podemos criar um modeloPersonRepository
, pronto para receber nossas queries CRUD.Crie uma classe
PersonRepository.java
ao lado daPersonEntity.java
.1 package com.mongodb; 2 3 import com.mongodb.client.MongoClient; 4 import com.mongodb.client.MongoCollection; 5 import jakarta.enterprise.context.ApplicationScoped; 6 7 8 public class PersonRepository { 9 10 private final MongoClient mongoClient; 11 private final MongoCollection<PersonEntity> coll; 12 13 public PersonRepository(MongoClient mongoClient) { 14 this.mongoClient = mongoClient; 15 this.coll = mongoClient.getDatabase("test").getCollection("persons", PersonEntity.class); 16 } 17 18 // CRUD methods will go here 19 20 }
Agora estamos quase prontos para criar nosso primeiro método CRUD. Vamos atualizar a classe
GreetingResource.java
padrão para corresponder ao nosso objetivo.- Renomeie o arquivo
GreetingResource.java
paraPersonResource.java
. - Na pasta
test
, renomeie também os arquivos de teste padrão paraPersonResourceIT.java
ePersonResourceTest.java
. - Atualize
PersonResource.java
assim:
1 package com.mongodb; 2 3 import jakarta.inject.Inject; 4 import jakarta.ws.rs.*; 5 import jakarta.ws.rs.core.MediaType; 6 7 8 9 10 public class PersonResource { 11 12 13 PersonRepository personRepository; 14 15 16 17 public String hello() { 18 return "Hello from Quarkus REST"; 19 } 20 21 // CRUD routes will go here 22 23 }
Observe que, com a anotação
@Path("/api")
, a URL do nosso serviço/hello
agora é /api/hello
.Como consequência, atualize
PersonResourceTest.java
para que nosso teste continue funcionando.1 package com.mongodb; 2 3 import io.quarkus.test.junit.QuarkusTest; 4 import org.junit.jupiter.api.Test; 5 6 import static io.restassured.RestAssured.given; 7 import static org.hamcrest.CoreMatchers.is; 8 9 10 class PersonResourceTest { 11 12 void testHelloEndpoint() { 13 given().when().get("/api/hello").then().statusCode(200).body(is("Hello from Quarkus REST")); 14 } 15 }
Todos os blocos de código estão agora em vigor. Podemos criar nossa primeira rota para poder criar uma nova pessoa.
No repositório, adicione o seguinte método que insere um
PersonEntity
e retorna o ObjectId
do documento inserido no formatoString
.1 public String add(PersonEntity person) { 2 return coll.insertOne(person).getInsertedId().asObjectId().getValue().toHexString(); 3 }
1 2 3 public String createPerson(PersonEntity person) { 4 return personRepository.add(person); 5 }
Sem reiniciar o projeto (lembre-se do modo dev?), Você deve ser capaz de testar esta rota.
1 curl -X POST http://localhost:8080/api/person \ 2 -H 'Content-Type: application/json' \ 3 -d '{"name": "John Doe", "age": 30}'
Isso deve retornar a
ObjectId
do novo documentoperson
.1 661dccf785cd323349ca42f7
1 RS [direct: primary] test> db.persons.find() 2 [ 3 { 4 _id: ObjectId('661dccf785cd323349ca42f7'), 5 age: 30, 6 name: 'John Doe' 7 } 8 ]
Agora, podemos ler todas as pessoas no banco de dados, por exemplo.
1 public List<PersonEntity> getPersons() { 2 return coll.find().into(new ArrayList<>()); 3 }
1 2 3 public List<PersonEntity> getPersons() { 4 return personRepository.getPersons(); 5 }
Agora podemos recuperar todas as pessoas em nosso banco de dados:
1 curl http://localhost:8080/api/persons
Isso retorna uma lista de pessoas:
1 [ 2 { 3 "id": "661dccf785cd323349ca42f7", 4 "name": "John Doe", 5 "age": 30 6 } 7 ]
É o aniversário de John Doe! Vamos aumentar a idade dele em um.
1 public long anniversaryPerson(String id) { 2 Bson filter = eq("_id", new ObjectId(id)); 3 Bson update = inc("age", 1); 4 return coll.updateOne(filter, update).getModifiedCount(); 5 }
1 2 3 public long anniversaryPerson( String id) { 4 return personRepository.anniversaryPerson(id); 5 }
É hora de testar essa festa:
1 curl -X PUT http://localhost:8080/api/person/661dccf785cd323349ca42f7
Isso retorna
1
, que é o número de documentos modificados. Se o ObjectId
fornecido não corresponder ao ID de uma pessoa, ele retornará 0
e o MongoDB não executará nenhuma atualização.Finalmente, é hora de excluir o John Doe...
1 public long deletePerson(String id) { 2 Bson filter = eq("_id", new ObjectId(id)); 3 return coll.deleteOne(filter).getDeletedCount(); 4 }
1 2 3 public long deletePerson( String id) { 4 return personRepository.deletePerson(id); 5 }
Vamos testar:
1 curl -X DELETE http://localhost:8080/api/person/661dccf785cd323349ca42f7
Novamente, ele retorna
1
que é o número de documentos excluídos.Agora que temos um aplicativo Quartokus em funcionamento com um serviço CRUD do MongoDB, é hora de experimentar todo o poder do Quakus.
Saia do modo de desenvolvedor simplesmente pressionando a tecla
q
no terminal relevante.É hora de construir o executável nativo que podemos usar na produção com GraalVM e experimentar o tempo de inicializaçãoinsanamente rápido.
Use esta linha de comando para construir diretamente com seu GraalVM local e outras dependências.
1 ./mvnw package -Dnative
Ou use a imagem do Docker que contém tudo o que você precisa:
1 ./mvnw package -Dnative -Dquarkus.native.container-build=true
O resultado final é um aplicativo nativo, pronto para ser iniciado, em sua pasta
target
.1 ./target/quarkus-mongodb-crud-1.0.0-SNAPSHOT-runner
No meu laptop, ele começa em apenas 0.019s! Lembre-se de quanto tempo o Spring Boot precisa para iniciar um aplicativo e responder às queries pela primeira vez ?!
Você pode ler mais sobre como o Quarkus torna esse milagre uma Realidade na documentação do container first.
Neste tutorial, exploramos como o Quekus e o MongoDB podem se unir para criar uma API RESTful ultra rápida com recursos de CRUD.
Agora fornecido com esses insights, você está pronto para criar API ultrarrápidas com o Quarkus, GraalVM e MongoDB. Mergulhe no Githubfornecido para obter mais detalhes.
Em caso de dúvidas, acesse nosso website da comunidade de desenvolvedores, no qual os engenheiros do MongoDB e a MongoDB Community ajudarão você a colocar em prática sua próxima grande ideia com o MongoDB.
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.