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 :
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.
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.
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 :
- Listes des grilles, personnes, capteurs, consommateurs, producteurs
- Consultation des personnes, grilles, capteurs
- Consultation des mesures et datapoints
- Consultation de la production / consommation totale
- Gestion des données des capteurs (
/ingress/windturbine
et UDP 12345) - Ajout, modification, suppression des personnes
- Modification des capteurs