Remarque :
- Ce tutoriel est disponible dans un environnement de laboratoire gratuit fourni par Oracle.
- Il utilise des exemples de valeur pour les informations d'identification, la location et les compartiments Oracle Cloud Infrastructure. A la fin de l'exercice, remplacez ces valeurs par celles propres à votre environnement cloud.
TASK 4 : Implémenter des requêtes de base de données et créer une application Micronaut
Dans cet atelier, vous allez implémenter des requêtes de base de données et créer une application Micronaut en local qui se connecte à Oracle Autonomous Database.
Temps estimé : 30 minutes
Contenu de la tâche
Dans cette tâche :
- Créer des entités de données Micronaut qui sont mises en correspondance avec les tables Oracle Database
- Définir des référentiels de données Micronaut pour implémenter des requêtes
- Exposer les contrôleurs Micronaut en tant qu'adresses REST
- Renseigner les données lors du démarrage de l'application
- Exécuter des tests d'intégration pour l'application Micronaut
Etape 1 : création d'entités de données Micronaut mises en correspondance avec des tables Oracle Database
Dans la tâche précédente, vous avez ajouté le script SQL qui créerait une table nommée OWNER et une table nommée PET une fois exécutée. Vous devez ensuite définir des classes d'entité qui peuvent être utilisées pour lire les données des tables de base de données.
-
Créez une classe d'entité qui représentera un élément
Ownerdans le packagesrc/main/java/com/example. Cliquez avec le bouton droit de la souris sursrc/main/java/com/examplepour développer le menu de contenu, sélectionnez New File, nommez-leOwner.javaet collez le code suivant :
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; } }L'annotation
@MappedEntitypermet d'indiquer que l'entité est mise en correspondance avec une table de base de données. Par défaut, il s'agit d'une table portant le même nom que la classe (dans ce cas,owner).Les colonnes de la table sont représentées par chaque propriété Java. Dans le cas ci-dessus, une colonne
idsera utilisée pour représenter la clé primaire et@GeneratedValueconfigurera le mapping pour prendre en compte l'utilisation d'une colonneidentitydans Autonomous Database.L'annotation
@Creatorest utilisée sur le constructeur qui sera utilisé pour instancier l'entité mise en correspondance et qui est également utilisée pour exprimer les colonnes requises. Dans ce cas, la colonnenameest requise et immuable tant que la colonneagen'est pas requise et peut être définie indépendamment à l'aide de la méthode setsetAge. -
Créez un fichier
Pet.javaqui représentera l'entitéPetpour modéliser une tablepetsoussrc/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 } }La classe
Petutilise une clé primaireUUIDrenseignée automatiquement pour illustrer les différentes approches de génération d'ID.Une relation entre la classe
Petet la classeOwnerest également définie à l'aide de l'annotation@Relation(Relation.Kind.MANY_TO_ONE), ce qui indique qu'il s'agit d'une relation n à n.Cela étant, il est temps de passer à la définition des interfaces de référentiel pour implémenter des requêtes.
Etape 2 : Définir des référentiels de données Micronaut pour implémenter des requêtes
Les données Micronaut prennent en charge la notion de définition d'interfaces qui implémentent automatiquement des requêtes SQL pour vous au moment de la compilation à l'aide du modèle de référentiel de données. Dans cette section, vous allez tirer parti de cette fonctionnalité de données Micronaut.
-
Créez un dossier distinct nommé
repositoriessoussrc/main/java/com/example. -
Définissez une nouvelle interface de référentiel qui s'étend à partir de
CrudRepositoryet est annotée avec@JdbcRepositoryà l'aide du dialecteORACLEdans un fichier appeléOwnerRepository.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); }L'interface
CrudRepositoryprend 2 types d'argument génériques. Le premier est le type de l'entité (dans ce cas,Owner) et le second est le type de l'ID (dans ce cas,Long).L'interface
CrudRepositorydéfinit des méthodes permettant de créer, lire, mettre à jour et supprimer des entités (CRUD) de la base de données à l'aide des insertions, sélections, mises à jour et suppressions SQL appropriées calculées pour vous au moment de la compilation. Pour plus d'informations, reportez-vous à Javadoc pour CrudRepository.Vous pouvez définir des méthodes dans l'interface qui effectuent des requêtes JDBC et gérer automatiquement tous les détails complexes pour vous, tels que la définition d'une sémantique de transaction correcte (transactions en lecture seule pour les requêtes), l'exécution de la requête et la mise en correspondance de l'ensemble de résultats avec la classe d'entité
Ownerdéfinie précédemment.La méthode
findByNamedéfinie ci-dessus produira automatiquement une requête telle queSELECT ID, NAME, AGE FROM OWNER WHERE NAME = ?au moment de la compilation.Pour plus d'informations sur les méthodes de requête et les types de requête que vous pouvez définir, reportez-vous à la documentation relative aux méthodes de requête dans la documentation Micronaut Data.
-
Une fois
OwnerRepositoryen place, définissez un autre référentiel et, cette fois, utilisez un objet de transfert de données (DTO) pour effectuer une requête optimisée. Par conséquent, vous devez d'abord créer la classe DTO dans un fichier nomméNameDTO.javasoussrc/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; } }Un DTO est un POJO simple qui vous permet de sélectionner uniquement les colonnes dont une requête particulière a besoin, produisant ainsi une requête plus optimisée.
-
Définissez le référentiel appelé
PetRepositorydans un fichier appeléPetRepository.javapour l'entitéPetqui utilise le DTO au même emplacementsrc/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); }Notez la méthode
listqui renvoie le DTO. Cette méthode sera à nouveau implémentée pour vous au moment de la compilation, mais cette fois-ci, au lieu d'extraire toutes les colonnes de la tablePet, elle extrait uniquement la colonnenameet toutes les autres colonnes que vous pouvez définir.L'annotation
@Joininterroge et instancie l'objet joint (Owner) et l'affecte au champOwnerde l'objet interrogéPet.La méthode
findByNameest également intéressante car elle utilise une autre caractéristique importante de Micronaut Data, qui est l'annotation@Join. Il vous permet de spécifier des chemins de jointure afin d'extraire exactement les données dont vous avez besoin via des jointures de base de données, ce qui permet d'obtenir des requêtes beaucoup plus efficaces.
Une fois les référentiels de données en place, passez à l'affichage des adresses REST.
Etape 3 : afficher les contrôleurs Micronaut en tant qu'adresses REST
Les adresses REST de Micronaut sont faciles à écrire et sont définies comme des contrôleurs (selon le modèle MVC).
-
Créez un dossier
controllerssoussrc/main/java/com/example/. -
Définissez une nouvelle classe
OwnerControllerdans un fichier nomméOwnerController.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); } }Une classe de contrôleur est définie avec l'annotation
@Controllerque vous pouvez utiliser pour définir l'URI racine avec lequel le contrôleur est mis en correspondance (dans ce cas,/owners).Notez l'annotation
@ExecuteOnutilisée pour indiquer à Micronaut que le contrôleur effectue une communication d'E/S avec une base de données. Par conséquent, les opérations doivent être exécutées sur le pool de threads d'E/S.La classe
OwnerControllerutilise l'injection de dépendance Micronaut pour obtenir une référence à l'interface de référentielOwnerRepositoryque vous avez définie précédemment et est utilisée pour implémenter deux adresses :/: l'adresse racine répertorie tous les propriétaires/{name}: la deuxième adresse utilise un modèle d'URI pour permettre la recherche d'un propriétaire par son nom. La valeur de la variable d'URI{name}est fournie en tant que paramètre de la méthodebyName.
-
Définissez ensuite une autre adresse REST appelée
PetControllerdans un fichier appeléPetController.javasoussrc/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); } }Cette fois,
PetRepositoryest injecté pour afficher la liste des animaux de compagnie et des animaux de compagnie par nom.
Etape 4 : Renseigner les données lors du démarrage de l'application
L'étape suivante consiste à renseigner certaines données d'application au démarrage. Pour ce faire, vous pouvez utiliser des événements d'application Micronaut.
Ouvrez la classe src/main/java/com/example/Application.java et remplacez le contenu initial du fichier par ce qui suit :
![]()
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));
}
}
Le constructeur est modifié pour injecter les définitions du référentiel de sorte que les données puissent être conservées.
Enfin, la méthode init est annotée avec @EventListener avec un argument pour recevoir StartupEvent. Cet événement est appelé une fois l'application en cours d'exécution et peut être utilisé pour la persistance des données lorsque l'application est prête à le faire.
Le reste de l'exemple illustre l'enregistrement de quelques entités à l'aide de la méthode saveAll de l'interface CrudRepository.
Notez que javax.transaction.Transactional est déclaré sur la méthode, ce qui garantit que Micronaut Data encapsule l'exécution de la méthode init dans une transaction JDBC qui est annulée si une exception survient lors de l'exécution de la méthode.
Etape 5 : exécutez des tests d'intégration pour l'application Micronaut.
L'application a déjà été configurée avec un seul test qui vérifie si l'application peut démarrer correctement (et testera donc la logique de la méthode init définie dans la section précédente).
-
Dans la barre de navigation supérieure, accédez à Terminal, puis à New Terminal.
-
Exécutez l'objectif
testpour exécuter les tests :
./mvnw test
Si vous utilisez Gradle, vous pouvez également exécuter la tâche
test:
./gradlew testLe message BUILD SUCCESS doit s'afficher à la fin de l'exécution du test.
Vous pouvez maintenant passer à la tâche suivante.