Observação:
- Este tutorial está disponível em um ambiente de laboratório gratuito fornecido pela Oracle.
- Ele usa valores de exemplo para credenciais, tenancy e compartimentos do Oracle Cloud Infrastructure. Ao concluir seu laboratório, substitua esses valores por valores específicos do seu ambiente de nuvem.
TAREFA 4: Implementar Consultas de Banco de Dados e Criar um Aplicativo Micronaut
Neste laboratório, você implementará consultas de banco de dados e criará um aplicativo Micronaut localmente que se conecta ao Oracle Autonomous Database.
Tempo Estimado: 30 minutos
Conteúdo da Tarefa
Nesta tarefa você:
- Criar entidades de Dados Micronaut que mapeiam para tabelas do Oracle Database
- Defina repositórios de dados Micronaut para implementar consultas
- Expor Controladores Micronaut como pontos finais REST
- Preencher Dados na Inicialização do Aplicativo
- Executar testes de integração para o aplicativo Micronaut
Etapa 1: Crie Entidades de Dados Micronaut que Mapeiem para Tabelas do Oracle Database
Na tarefa anterior, você adicionou o script SQL que criaria uma tabela chamada OWNER e uma tabela chamada PET uma vez executada. Em seguida, você precisa definir classes de entidade que podem ser usadas para ler dados das tabelas do banco de dados.
-
Crie uma classe de entidade que representará um
Ownerno pacotesrc/main/java/com/example. Clique com o botão direito do mouse emsrc/main/java/com/examplepara expandir o menu de conteúdo, selecione Novo Arquivo, nomeie-o comoOwner.javae cole o seguinte código:
package com.example; import io.micronaut.core.annotation.Creator; import io.micronaut.data.annotation.GeneratedValue; import io.micronaut.data.annotation.Id; import io.micronaut.data.annotation.MappedEntity; @MappedEntity public class Owner { // The ID of the class uses a generated sequence value @Id @GeneratedValue private Long id; private final String name; private int age; // the constructor reads column values by the name of each constructor argument @Creator public Owner(String name) { this.name = name; } // each property of the class maps to a database column public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } }A anotação
@MappedEntityé usada para indicar que a entidade está mapeada para uma tabela de banco de dados. Por padrão, essa será uma tabela usando o mesmo nome da classe (nesse caso,owner).As colunas da tabela são representadas por cada propriedade Java. No caso acima, uma coluna
idserá usada para representar a chave primária e@GeneratedValueconfigurará o mapeamento para assumir o uso de uma colunaidentityno Autonomous Database.A anotação
@Creatoré usada no construtor que será usado para instanciar a entidade mapeada e também para expressar colunas obrigatórias. Nesse caso, a colunanameé obrigatória e imutável, enquanto a colunaagenão é obrigatória e pode ser definida de forma independente usando o settersetAge. -
Crie um arquivo
Pet.javaque representará a entidadePetpara modelar uma tabelapetemsrc/main/java/com/example:
package com.example; import io.micronaut.core.annotation.Creator; import io.micronaut.core.annotation.Nullable; import io.micronaut.data.annotation.AutoPopulated; import io.micronaut.data.annotation.Id; import io.micronaut.data.annotation.MappedEntity; import io.micronaut.data.annotation.Relation; import java.util.UUID; @MappedEntity public class Pet { // This class uses an auto populated UUID for the primary key @Id @AutoPopulated private UUID id; private final String name; // A relation is defined between Pet and Owner @Relation(Relation.Kind.MANY_TO_ONE) private final Owner owner; private PetType type = PetType.DOG; // The constructor defines the columns to be read @Creator public Pet(String name, @Nullable Owner owner) { this.name = name; this.owner = owner; } public Owner getOwner() { return owner; } public String getName() { return name; } public UUID getId() { return id; } public void setId(UUID id) { this.id = id; } public PetType getType() { return type; } public void setType(PetType type) { this.type = type; } public enum PetType { DOG, CAT } }Observe que a classe
Petusa umUUIDpreenchido automaticamente como a chave primária para demonstrar diferentes abordagens à geração de ID.Um relacionamento entre a classe
Pete a classeOwnertambém é definido usando a anotação@Relation(Relation.Kind.MANY_TO_ONE), indicando que esse é um relacionamento muitos para um.Com isso, chegou a hora de definir interfaces de repositório para implementar consultas.
Etapa 2: Definir Repositórios de Dados Micronaut para Implementar Consultas
O Micronaut Data suporta a noção de definição de interfaces que implementam automaticamente consultas SQL para você no momento da compilação usando o padrão de repositório de dados. Nesta seção você aproveitará esse recurso de Dados Micronaut.
-
Crie uma pasta separada chamada
repositoriesemsrc/main/java/com/example. -
Defina uma nova interface de repositório que se estende de
CrudRepositorye seja anotada com@JdbcRepositoryusando o dialetoORACLEem um arquivo chamadoOwnerRepository.java:
package com.example.repositories; import com.example.Owner; import io.micronaut.data.jdbc.annotation.JdbcRepository; import io.micronaut.data.model.query.builder.sql.Dialect; import io.micronaut.data.repository.CrudRepository; import java.util.List; import java.util.Optional; // The @JdbcRepository annotation indicates the database dialect @JdbcRepository(dialect = Dialect.ORACLE) public interface OwnerRepository extends CrudRepository<Owner, Long> { @Override List<Owner> findAll(); // This method will compute at compilation time a query such as // SELECT ID, NAME, AGE FROM OWNER WHERE NAME = ? Optional<Owner> findByName(String name); }A interface
CrudRepositoryusa 2 tipos de argumentos genéricos. O primeiro é o tipo da entidade (nesse caso,Owner) e o segundo é o tipo do ID (nesse caso,Long).A interface
CrudRepositorydefine métodos que permitem criar, ler, atualizar e excluir entidades (CRUD) do banco de dados com as inserções, seleções, atualizações e exclusões SQL apropriadas calculadas para você no momento da compilação. Para obter mais informações, consulte o Javadoc para CrudRepository.Você pode definir métodos na interface que executam consultas JDBC e tratar automaticamente todos os detalhes intrincados para você, como definir semântica de transação correta (transações somente leitura para consultas), executar a consulta e mapear o conjunto de resultados para a classe de entidade
Ownerdefinida anteriormente.O método
findByNamedefinido acima produzirá uma consulta comoSELECT ID, NAME, AGE FROM OWNER WHERE NAME = ?automaticamente no momento da compilação.Para obter mais informações sobre métodos de consulta e os tipos de consultas que você pode definir, consulte a documentação para métodos de consulta na documentação do Micronaut Data.
-
Com o
OwnerRepositoryinstalado, defina outro repositório e desta vez usando um objeto de transferência de dados (DTO) para executar uma consulta otimizada. Então, primeiro, você precisa criar a classe DTO em um arquivo chamadoNameDTO.javaemsrc/main/java/com/example/repositories:
package com.example.repositories; import io.micronaut.core.annotation.Introspected; @Introspected public class NameDTO { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }DTO é uma POJO simples que permite selecionar apenas as colunas necessárias a uma consulta específica, produzindo uma consulta mais otimizada.
-
Defina o repositório chamado
PetRepositoryem um arquivo chamadoPetRepository.javapara a entidadePetque usa o DTO no mesmo localsrc/main/java/com/example/repositories:
package com.example.repositories; import com.example.Pet; import io.micronaut.data.annotation.Join; import io.micronaut.data.jdbc.annotation.JdbcRepository; import io.micronaut.data.model.query.builder.sql.Dialect; import io.micronaut.data.repository.PageableRepository; import java.util.List; import java.util.Optional; import java.util.UUID; @JdbcRepository(dialect = Dialect.ORACLE) public interface PetRepository extends PageableRepository<Pet, UUID> { List<NameDTO> list(); @Join("owner") Optional<Pet> findByName(String name); }Anote o método
listque retorna o DTO. Esse método será implementado novamente para você no momento da compilação, mas dessa vez, em vez de recuperar todas as colunas da tabelaPet, ele só recuperará a colunanamee quaisquer outras colunas que você possa definir.A anotação
@Joinconsultará e instanciará o objeto unido (Owner) e o designará ao campoOwnerdoPetconsultado.O método
findByNametambém é interessante, pois usa outro recurso importante dos Dados Micronaut, que é a anotação@Join. Ele permite especificar caminhos de junção para que você recupere exatamente os dados necessários por meio de junções de banco de dados, resultando em consultas muito mais eficientes.
Com os repositórios de dados instalados, passe para a exposição de pontos finais REST.
Etapa 3: Exponha Controladores Micronaut como Pontos Finais REST
Os pontos finais REST no Micronaut são fáceis de gravar e são definidos como controladores (de acordo com o padrão MVC).
-
Crie uma pasta
controllersemsrc/main/java/com/example/. -
Defina uma nova classe
OwnerControllerem um arquivo chamadoOwnerController.java:
package com.example.controllers; import java.util.List; import java.util.Optional; import javax.validation.constraints.NotBlank; import com.example.Owner; import com.example.repositories.OwnerRepository; import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.micronaut.scheduling.TaskExecutors; import io.micronaut.scheduling.annotation.ExecuteOn; @Controller("/owners") @ExecuteOn(TaskExecutors.IO) class OwnerController { private final OwnerRepository ownerRepository; OwnerController(OwnerRepository ownerRepository) { this.ownerRepository = ownerRepository; } @Get("/") List<Owner> all() { return ownerRepository.findAll(); } @Get("/{name}") Optional<Owner> byName(@NotBlank String name) { return ownerRepository.findByName(name); } }Uma classe de controlador é definida com a anotação
@Controllerque você pode usar para definir o URI raiz para o qual o controlador mapeia (nesse caso,/owners).Observe a anotação
@ExecuteOnque é usada para informar à Micronaut que o controlador executa a comunicação de E/S com um banco de dados e, portanto, as operações devem executar no pool de threads de E/S.A classe
OwnerControllerusa a Injeção de dependência Micronaut para obter uma referência à interface do repositórioOwnerRepositorydefinida anteriormente e é usada para implementar dois pontos finais:/- O ponto final raiz lista todos os proprietários/{name}- O segundo ponto final usa um modelo de URI para permitir procurar um proprietário por nome. O valor da variável de URI{name}é fornecido como um parâmetro para o métodobyName.
-
Em seguida, defina outro ponto final REST chamado
PetControllerem um arquivo chamadoPetController.javaemsrc/main/java/com/example/controllers:
package com.example.controllers; import java.util.List; import java.util.Optional; import com.example.repositories.NameDTO; import com.example.Pet; import com.example.repositories.PetRepository; import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.micronaut.scheduling.TaskExecutors; import io.micronaut.scheduling.annotation.ExecuteOn; @ExecuteOn(TaskExecutors.IO) @Controller("/pets") class PetController { private final PetRepository petRepository; PetController(PetRepository petRepository) { this.petRepository = petRepository; } @Get("/") List<NameDTO> all() { return petRepository.list(); } @Get("/{name}") Optional<Pet> byName(String name) { return petRepository.findByName(name); } }Desta vez, o
PetRepositoryé injetado para expor uma lista de animais de estimação e animais de estimação por nome.
Etapa 4: Preencher Dados na Inicialização do Aplicativo
A próxima etapa é preencher alguns dados do aplicativo na inicialização. Para fazer isso, você pode usar Eventos de aplicativo Micronaut.
Abra a classe src/main/java/com/example/Application.java e substitua o conteúdo do arquivo inicial pelo seguinte:
![]()
package com.example;
import com.example.repositories.OwnerRepository;
import com.example.repositories.PetRepository;
import io.micronaut.context.event.StartupEvent;
import io.micronaut.runtime.Micronaut;
import io.micronaut.runtime.event.annotation.EventListener;
import jakarta.inject.Singleton;
import javax.transaction.Transactional;
import java.util.Arrays;
@Singleton
public class Application {
private final OwnerRepository ownerRepository;
private final PetRepository petRepository;
Application(OwnerRepository ownerRepository, PetRepository petRepository) {
this.ownerRepository = ownerRepository;
this.petRepository = petRepository;
}
public static void main(String[] args) {
Micronaut.run(Application.class);
}
@EventListener
@Transactional
void init(StartupEvent event) {
// clear out any existing data
petRepository.deleteAll();
ownerRepository.deleteAll();
// create data
Owner fred = new Owner("Fred");
fred.setAge(45);
Owner barney = new Owner("Barney");
barney.setAge(40);
ownerRepository.saveAll(Arrays.asList(fred, barney));
Pet dino = new Pet("Dino", fred);
Pet bp = new Pet("Baby Puss", fred);
bp.setType(Pet.PetType.CAT);
Pet hoppy = new Pet("Hoppy", barney);
petRepository.saveAll(Arrays.asList(dino, bp, hoppy));
}
}
Observe que o construtor é modificado para a dependência injetar as definições do repositório para que os dados possam ser persistidos.
Por fim, o método init é anotado com @EventListener com um argumento para receber um StartupEvent. Este evento é chamado assim que o aplicativo está ativo e em execução e pode ser usado para persistir dados quando seu aplicativo estiver pronto para fazer isso.
O restante do exemplo demonstra como salvar algumas entidades usando o método saveAll da interface CrudRepository.
Observe que javax.transaction.Transactional é declarado no método que garante que os Dados Micronaut tragam a execução do método init em uma transação JDBC que é submetida a rollback se ocorrer uma exceção durante a execução do método.
Etapa 5: Executar Testes de Integração para o Aplicativo Micronaut
O aplicativo já foi configurado com um único teste que verifica se o aplicativo pode ser inicializado com sucesso (e, portanto, testará a lógica do método init definido na seção anterior).
-
Na navegação superior, vá para Terminal e Novo Terminal.
-
Execute a meta
testpara executar testes:
./mvnw test
Como alternativa, se você estiver usando Gradle, execute a tarefa
test:
./gradlew testVocê deverá ver a mensagem SUILD SUCCESS no final da execução do teste.
Agora você pode passar para a próxima tarefa.