Persistance

Ajout de la persistance

Dans l’activité précédente nous avons commencé à prendre en main Vert.x et ajouté des routes sans persistance pour notre application. Maintenant nous allons ajouter une persistance pour nos entités en connectant notre application à une base de données.

Pour la suite de l’application, nous utiliserons la base de données dont les schémas conceptuel et logique sont les suivants :

schema conceptuel de l’application

schema logique de l’application

Mise à jour du squelette de code

Vous trouverez en dessous 3 archives de code. La première, smart_grid_demo.zip est une démo du système complet que vous pouvez utiliser comme référence pour votre serveur. Les deux suivantes reprennent le squelette de l’activité 3 en ajoutant la persistance et une base de données. Téléchargez une des deux archives et dézippez la. Vous pourrez ensuite reprendre vos handlers codés dans l’activité précédence.

Téléchargez les codes

Comme dans l’activité de BDD, une fois les services lancés avec docker compose up -d vous pourrez accéder à une interface de gestion de la base de données ici, le Système est PostgreSQL, les identifiant, mot de passe et base de données sont test.

Pour la suite nous vous recommandons de coder directement des handlers qui intègrent la persistance et de vous répartir le travail pour avancer sur plusieurs routes en même temps.

Pensez également associer l’EntityManager à vos handlers pour que ceux-ci puissent interagir avec la base de données. Regardez comment cela est fait dans ExampleHandler.java.

Structure des nouveaux codes

En ouvrant le projet, vous avez un nouveau package model contenant l’ensemble des classes correspondant aux entités de la smart grid. Ce sont ces classes que vous manipulerez pour interagir avec la base de données. Pour limiter les redondances de codes dans vos handlers, vous pouvez ajouter des méthodes à ces classes (ex. toJSON()).

Vous pouvez aussi voir, dans le package server un nouveau handler, ExampleHandler.java qui utilise la base de données. En plus des méthodes vues lors de l’activité 5 de BDD, vous pouvez constater l’utilisation de requête SQL brutes.

Long nbSensors = (Long)db.createNativeQuery("SELECT count(*) FROM sensor").getSingleResult();

Ici, des requêtes SQL directes sont effectuées depuis le code Java. Ces requêtes natives sont très flexibles car vous pouvez écrire n’importe quel code SQL. Cependant, il peut être compliqué de récupérer des objets de notre package model avec. C’est pourquoi nous vous conseillons de n’utiliser ces requêtes natives que lorsque vous souhaitez récupérer des données primitives (entiers, flottants, chaines de caractères, …). Typiquement, pour les routes /persons, /sensors, /grids, /grid/:id/production ou /grid/:id/consumption.

Lorsque vous avez besoin de récupérer un objet, nous vous conseillons d’utiliser la méthode find() vue dans l’activité 5 de BDD. La méthode find() fait quelque chose de similaire au bloc de code suivant :

Sensor s = (Sensor) db
    .createNativeQuery("SELECT * FROM sensor WHERE id = ?", Sensor.class)
    .setParameter(1, 4)
    .getSingleResult();

Notez le ? dans la requête, c’est un paramètre qui sera remplacé par une valeur avec la méthode setParameter(index, value). Celle-ci sert à se protéger des attaques par injection SQL. En effet, lorsque nous préparons une requête de cette façon, le système s’assure que les valeurs que nous injectons sont bien formées et ne causent pas de soucis.

Enfin, l’appel à la méthode getSingleResult() exécute la requête et nous retourne la valeur correspondante. Si la requête retourne plusieurs résultats, il faut utiliser la méthode getResultList() qui retourne une liste contenant les résultats.

Différence entre find() et le code avec createNativeQuery

Notez que le code précédent va récupérer une capteur depuis la base de données cependant seules les données présentes dans la table sensor seront présentes dans notre capteur. Par exemple, le capteur avec l’id 4 est un chargeur de voiture. En utilisant find(4, Sensor.class) nous obtenons notre objet sensor de type EVCharger avec toutes les propriétés. Tandis que le code ci-dessus initialisera notre EVcharger qu’avec les informations contenues dans la table sensor et rien des tables Producer ou EV_Charger.

Nous aurons donc un objet partiellement initialisé !

La suite

C’est à vous de continuez à implémenter les routes de façon à ce que votre backend se comporte correctement. Les routes sont spécifiées dans l’activité précédente.

Pour aller du plus simple au plus complexe, nous vous conseillons de réaliser les routes dans l’ordre suivant :

  1. Listes des grilles, personnes, capteurs, consommateurs, producteurs
  2. Consultation des personnes, grilles, capteurs
  3. Consultation des mesures et datapoints
  4. Consultation de la production / consommation totale
  5. Gestion des données des capteurs (/ingress/windturbine et UDP 12345)
  6. Ajout, modification, suppression des personnes
  7. Modification des capteurs