L'analyse des schémas de bases de données offre une base solide pour l'ingénierie logicielle déclarative. En imposant une convention de préfixage systématique sur les noms de colonnes (notamment int_, str_, dtm_, dec_), il devient possible d'extraire automatiquement les métadonnées et de produire des couches de données cohérentes. Cette approche réduit considérablement la quantité de code redondant tout en garantissant une synchronisation étroite entre le stockage relationnel et les objets applicatifs.
Implémentation VB.NET
Le modèle suit une hiérarchie héritée où chaque entité gère son état interne et sa sérialisation vers le système relationnel.
Namespace Persistance.Modeles
Public Class ClientEntite
Inherits ObjetBase
Private Const NomTable As String = "tbl_client"
Public Shared Function ChercherUn(ByVal pId As Integer) As ClientEntite
Dim instance As New ClientEntite With {.Identifiant = pId}
instance.Charger()
Return instance
End Function
#Region "Champs Propriétés"
Public Property Identifiant As Integer = 0
Public Property Nom As String = String.Empty
Public Property DateNaissance As DateTime = Date.MinValue
Public Property Age As Integer = 0
Public Property Solde As Decimal = 0D
Public Property Annotation As String = String.Empty
#End Region
#Region "Logique Métier"
Private Sub AppliquerChargement(row As DataRow, cibles As ClientEntite)
With cibles
.Identifiant = If(row.IsNull("int_Id"), 0, row.Item("int_Id"))
.Nom = If(row.IsNull("str_Nom"), String.Empty, row.Item("str_Nom").ToString())
.DateNaissance = If(row.IsNull("dtm_Naissance"), DateTime.MinValue, CType(row.Item("dtm_Naissance"), DateTime))
.Age = If(row.IsNull("int_Age"), 0, row.Item("int_Age"))
.Solde = If(row.IsNull("dec_Solde"), 0D, row.Item("dec_Solde"))
.Annotation = If(row.IsNull("str_Annotation"), String.Empty, row.Item("str_Annotation").ToString())
.EtatEnregistrement = EtatEntite.AucunChangement
End With
End Sub
Public Overrides Function Charger(Optional ByVal donnees As DataTable = Nothing) As List(Of ClientEntite)
MyBase.Charger()
Dim resultat As New List(Of ClientEntite)
Dim origineSGBD As Boolean = False
If donnees Is Nothing Then
Using req As IDbCommand = Connecteur.CreerRequête()
req.CommandText = "SELECT * FROM tbl_client WHERE int_Id=@pId"
Connecteur.AjouterParamètre(req, "@pId", Identifiant)
donnees = Connecteur.ExécuterDataTable(req)
End Using
origineSGBD = True
End If
For Each ligne As DataRow In donnees.Rows
Dim cible As ClientEntite = If(origineSGBD, Me, New ClientEntite)
AppliquerChargement(ligne, cible)
resultat.Add(cible)
Next
Return resultat
End Function
Public Function VersListe(ByVal tableSource As DataTable) As IEnumerable(Of ObjetBase)
Dim collection As New List(Of ClientEntite)()
If tableSource IsNot Nothing Then
For Each ligne As DataRow In tableSource.Rows
Dim obj As New ClientEntite
AppliquerChargement(ligne, obj)
collection.Add(obj)
Next
End If
Return collection
End Function
Public Overrides Function Persistre() As Integer
MyBase.Persistre()
Dim cmd As IDbCommand = Connecteur.CreerRequête()
Select Case Me.EtatEnregistrement
Case EtatEntite.Nouveau
cmd.CommandText = "INSERT INTO tbl_client (str_Nom,dtm_Naissance,int_Age,dec_Solde,str_Annotation) VALUES (@pNom,@pDate,@pAge,@pSolde,@pNote)"
Connecteur.AjouterParamètre(cmd, "@pNom", Nom)
Connecteur.AjouterParamètre(cmd, "@pDate", DateNaissance)
Connecteur.AjouterParamètre(cmd, "@pAge", Age)
Connecteur.AjouterParamètre(cmd, "@pSolde", Solde)
Connecteur.AjouterParamètre(cmd, "@pNote", Annotation)
Connecteur.ExecuterSansResultat(cmd)
Identifiant = Connecteur.RécupérerIdentifiant("tbl_client", "int_Id")
Me.EtatEnregistrement = EtatEntite.AucunChangement
Case EtatEntite.Modifié
cmd.CommandText = "UPDATE tbl_client SET str_Nom=@pNom, dtm_Naissance=@pDate, int_Age=@pAge, dec_Solde=@pSolde, str_Annotation=@pNote WHERE int_Id=@pId"
Connecteur.AjouterParamètre(cmd, "@pNom", Nom)
Connecteur.AjouterParamètre(cmd, "@pDate", DateNaissance)
Connecteur.AjouterParamètre(cmd, "@pAge", Age)
Connecteur.AjouterParamètre(cmd, "@pSolde", Solde)
Connecteur.AjouterParamètre(cmd, "@pNote", Annotation)
Connecteur.AjouterParamètre(cmd, "@pId", Identifiant)
Connecteur.ExecuterSansResultat(cmd)
Me.EtatEnregistrement = EtatEntite.AucunChangement
Case EtatEntite.Supprimé
cmd.CommandText = "DELETE FROM tbl_client WHERE int_Id=@pId"
Connecteur.AjouterParamètre(cmd, "@pId", Identifiant)
Connecteur.ExecuterSansResultat(cmd)
End Select
cmd.Dispose()
Return 0
End Function
#End Region
End Class
End Namespace
Modèle Android Java (SQLite)
Sous l'environnement mobile, l'extraction de curseurs et la manipulation de ContentValues sont centralisées dans des méthodes utilitaires dédiées au transfert d'état.
package io.app.modeles;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import android.content.ContentValues;
import android.database.Cursor;
import io.app.base.BaseModele;
import io.app.enums.EtatModele;
import io.app.utils.FormatteurDate;
public class ModeleClient extends BaseModele {
public static final String NOM_TABLE = "tbl_client";
private int identifiant = 0;
private String nom = "";
private Date dateNaissance = FormatteurDate.getDateMinimale();
private int age = 0;
private BigDecimal solde = new BigDecimal(0);
private String annotation = "";
public ModeleClient() { super(); }
// Accès mutateurs avec détection de changement
public int getIdentifiant() { return identifiant; }
public void setIdentifiant(int val) {
if (this.identifiant != val) { this.identifiant = val; notifierModification(); }
}
public String getNom() { return nom; }
public void setNom(String val) {
if (!nom.equalsIgnoreCase(val)) { this.nom = val; notifierModification(); }
}
public Date getDateNaissance() { return dateNaissance; }
public void setDateNaissance(Date val) {
if (val != null && dateNaissance.getTime() != val.getTime()) { this.dateNaissance = val; notifierModification(); }
}
public int getAge() { return age; }
public void setAge(int val) {
if (age != val) { age = val; notifierModification(); }
}
public BigDecimal getSolde() { return solde; }
public void setSolde(BigDecimal val) {
if (!solde.compareTo(val) == 0) { solde = val; notifierModification(); }
}
public String getAnnotation() { return annotation; }
public void setAnnotation(String val) {
if (!annotation.equalsIgnoreCase(val)) { annotation = val; notifierModification(); }
}
private ContentValues construireValuers() {
ContentValues valeurs = new ContentValues();
valeurs.put("int_Id", identifiant);
valeurs.put("str_Nom", nom);
valeurs.put("dtm_Naissance", FormatteurDate.formater(dateNaissance));
valeurs.put("int_Age", age);
valeurs.put("dec_Solde", solde);
valeurs.put("str_Annotation", annotation);
return valeurs;
}
private ModeleClient mapperCurseur(Cursor cur) {
ModeleClient instance = new ModeleClient();
instance.setIdentifiant(cur.getInt(cur.getColumnIndex("int_Id")));
instance.setNom(cur.getString(cur.getColumnIndex("str_Nom")));
instance.setDateNaissance(FormatteurDate.parse(cur.getString(cur.getColumnIndex("dtm_Naissance"))));
instance.setAge(cur.getInt(cur.getColumnIndex("int_Age")));
instance.setAnnotation(cur.getString(cur.getColumnIndex("str_Annotation")));
instance.setSolde(new BigDecimal(cur.getDouble(cur.getColumnIndex("dec_Solde"))));
instance.setEtatModele(EtatModele.AucunChangement);
return instance;
}
@Override
public void sauvegarder() {
ContentValues cvs;
switch (getEtatModele()) {
case NOUVEAU_ENREGISTREMENT:
cvs = construireValuers();
cvs.remove("int_Id");
GestionnaireBdd.insérer(NOM_TABLE, cvs);
identifiant = GestionnaireBdd.obtenirIdentifiant(NOM_TABLE);
setEtatModele(EtatModele.AucunChangement);
break;
case MODIFIÉ:
cvs = construireValuers();
GestionnaireBdd.modifier(NOM_TABLE, cvs, "int_Id=?", new String[]{String.valueOf(identifiant)});
setEtatModele(EtatModele.AucunChangement);
break;
case SUPPRIMÉ:
GestionnaireBdd.supprimer(NOM_TABLE, "int_Id=?", new String[]{String.valueOf(identifiant)});
break;
default: break;
}
}
public List<modeleclient> récupérerTous() {
List<modeleclient> liste = new ArrayList<>();
Cursor cur = GestionnaireBdd.interroger("SELECT * FROM " + NOM_TABLE);
while (cur.moveToNext()) {
liste.add(mapperCurseur(cur));
}
cur.close();
return liste;
}
public ModeleClient obtenirInstance() {
return obtenirParId(identifiant);
}
public ModeleClient obtenirParId(int pId) {
ModeleClient résultat = null;
String requête = String.format("SELECT * FROM %s WHERE int_Id=?", NOM_TABLE);
Cursor cur = GestionnaireBdd.interroger(requête, new String[]{String.valueOf(pId)});
if (cur.moveToFirst()) {
résultat = mapperCurseur(cur);
}
cur.close();
return résultat;
}
}
</modeleclient></modeleclient>
Persistence Node.js / Express
Côté serveur, la génération s'apuie sur des contrôleurs asynchrones qui traduisent les intentions d'interface en requêtes paramétrées sécurisées.
// Routing principal
const express = require('express');
const router = express.Router();
const { exécuterOpérationClient } = require('./gestionnaires/dbClient');
router.post('/client', async (req, res, next) => {
try {
const actionDemandée = req.body.action || "Rechercher";
const résultats = await exécuterOpérationClient(actionDemandée, req.body);
res.json(résultats);
} catch (erreur) {
res.status(500).json({ erreur: "Erreur système", détails: erreur.message });
}
});
module.exports = router;
// Couche DAL (Data Access Layer)
const poolBdd = require('./config/poolConnexion');
async function exécuterOpérationClient(action, données) {
let requêteSQL = "";
let paramètres = [];
let contexte = "";
switch (action.toLowerCase()) {
case "ajouter":
requêteSQL = "INSERT INTO tbl_client (str_Nom, dtm_Naissance, int_Age, dec_Solde, str_Annotation) VALUES (?, ?, ?, ?, ?)";
paramètres = [données.strNom, données.dtmNaissance, données.intAge, données.decSolde, données.strAnnotation];
contexte = "Insertion client";
break;
case "modifier":
requêteSQL = "UPDATE tbl_client SET str_Nom=?, dtm_Naissance=?, int_Age=?, dec_Solde=?, str_Annotation=? WHERE int_Id=?";
paramètres = [données.strNom, données.dtmNaissance, données.intAge, données.decSolde, données.strAnnotation, données.intId];
contexte = "Mise à jour client";
break;
case "supprimer":
requêteSQL = "DELETE FROM tbl_client WHERE int_Id=?";
paramètres = [données.intId];
contexte = "Suppression client";
break;
default:
requêteSQL = "SELECT * FROM tbl_client";
paramètres = [];
contexte = "Lecture globale";
break;
}
return new Promise((résoudre, rejeter) => {
poolBdd.query(requêteSQL, paramètres, (err, lignes) => {
if (err) return rejeter(new Error(contexte + " échoué : " + err.message));
résoudre({ succès: true, opération: contexte, données: lignes });
});
});
}
module.exports = { exécuterOpérationClient };
Ces architectures démontrant comment l'introspection de schéma pilote la création de squelettes de persistance peuvent être étendues à d'autres paradigmes ou extensions de tables sans modification manuelle des flux de travail existants.