:!: en cours de construction :!:

Mappings entre modèle Objet et modèle Relationnel

mais aussi avec les modèles arborescents

Persistance des objets, kesako ?

  • Comment faire pour :
    • Utiliser des objets au niveau métier
    • Pouvoir sauver et récupérer des objets
    • Mettre à jour des objets de manière pérenne

Utilisation d’un SGBD pour stocker les objets

  • SGBD: fiable, requêtes, concurrence
  • Ecrire du code pour :
    • Sauver un nouvel objet dans la BD
    • Sauver les modifications d’un objet
    • Récupérer un objet à partir de la BD
    • Faire des mises à jour pérennes

Rappel: comment accéder aux données de manière classique

  • Bibliothèque pour accéder au SGBD
    (e.g. JDBC, Python Database API)
  • Exécuter une requête
  • Itérer sur le résultat
    • Traiter chaque ligne

Exemple en Java

Statement s = connection.createStatement();
ResultSet rs = s.executeQuery(
    "SELECT * FROM etudiant WHERE formation='m1info'");
while(rs.next()) {
    String nom = rs.getString("nom");
    long id = rs.getLong("id");
    System.out.println("Etudiant: "+nom+" ("+id+")");
}

Exemple en Python

cur = connection.cursor()
cur.execute("SELECT * FROM etudiant WHERE formation='m1info'")
for row in cur.fetchall():
    nom = row[1]  # nom
    id = row[0]  # numero
    print("Etudiant: " + nom + " (" + str(id) + ")")
connection.close()

Exemple en Java

classe Etudiant

public class Etudiant {

    private int numero;
    private String nom;
    private String formation;

    public Etudiant(int numero, String nom, String formation) {
        this.numero = numero;
        this.nom = nom;
        this.formation = formation;
    }

Méthode d’ajout en base

  public void save(Connection cnx) throws SQLException {
      PreparedStatement pstat = cnx.prepareStatement(
        "INSERT INTO etudiant(numero, nom, formation) "
        +"VALUES (?,?,?)");
      pstat.setInt(1, numero);
      pstat.setString(2, nom);
      pstat.setString(3, formation);
      pstat.executeUpdate();
  }

Méthode de mise à jour

  public void update(Connection cnx) throws SQLException {
      PreparedStatement pstat = cnx.prepareStatement(
        "UPDATE etudiant "
        +"SET nom=?, formation=? "
        +"WHERE numero=?");
      pstat.setString(1, nom);
      pstat.setString(2, formation);
      pstat.setInt(3, numero);
      pstat.executeUpdate();
  }

Recherche par numéro d’étudiant

  public static Etudiant getByNum(int num, Connection cnx)
    throws SQLException {
      PreparedStatement pstat = cnx.prepareStatement(
        "SELECT numero,nom,formation FROM etudiant "
        +"WHERE numero=?");
      pstat.setInt(1, num);
      ResultSet rs = pstat.executeQuery();
      if (rs.next()) {
          return new Etudiant(rs.getInt("numero"),
                              rs.getString("nom"),
                              rs.getString("formation")  );
      } else {
          return null;
      }
  }

Exemple en Python

Classe Etudiant

class Etudiant:
    def __init__(self, numero, nom, formation):
        self.numero = numero
        self.nom = nom
        self.formation = formation

Méthode d’ajout en base

    def save(self, cnx):
        cur = cnx.cursor()
        cur.execute(
            """
            INSERT INTO etudiant(numero,nom,formation)
            VALUES (%s,%s,%s)
            """,
            (self.numero, self.nom, self.formation),
        )

Méthode de mise à jour

    def update(self, cnx):
        cur = cnx.cursor()
        cur.execute(
            """
            UPDATE etudiant
            SET nom=%s, formation=%s
            WHERE numero=%s
            """,
            (self.nom, self.formation, self.numero),
        )

Recherche par numéro d’étudiant

def getEtudiantByNum(num, cnx):
    cur = cnx.cursor()
    cur.execute(
        """
        SELECT numero, nom, formation
        FROM etudiant
        WHERE numero=%s
        """,
        (num,))
    row = cur.fetchone()
    if row is not None:
        return Etudiant(row[0], row[1], row[2])
    else:
        return None

Avantages

  • Bonne maîtrise du comportement
  • Bonnes performances

À condition de bien développer

Inconvénients

  • Code lourd à maintenir
  • Certaines fonctionnalités sont pénibles à implémenter
    (transactions, caches, parcours d’un graphe d’objets, …)
  • Pas de langage de haut niveau pour interroger les objets stockés

Cadre applicatif (framework) dédié

  • Attaquer le problème dans sa généralité
  • Limiter la quantité de code à écrire
  • Proposer des optimisations (e.g. caches)
  • Fournir un langage de haut niveau
  • Encapsuler les interactions avec la source de données
    • Pouvoir changer ainsément la source de données

=> ORM: Object Relational Mapping

Implémentation en Java

  • Java Persistence API (JPA)
    • Description des liens classes/tables
    • API EntityManager
  • Fournisseurs (implémentations) de JPA

Implémentations en Python

Pas d’API standardisée

ORM: Principes

Décrire les liens entre

  • les structures en mémoires
    (classes, pointeurs)
  • les structures de persistence
    (tables, contraintes)

ORM: Principes

Traduire

  • des demandes d’accès
    • à un champ (autre classe, collection)
    • pour récupérer un objet particulier
  • en requêtes (SQL)

ORM: Principes

Gérer les objets

Instancier des objets
selon le résultats des requêtes

Exécuter des requêtes
pour mettre à jour les données en base

Liens classes ↔︎ schéma

Cas simple

  • classe ↔︎ table
  • objet (instance) ↔︎ n-uplet dans une table
  • champ ↔︎ attribut
  • type SQL ↔︎ type Java ou Python (ou …)

Exemple

Etudiantnumero: 12345678nom: “Toto”formation: “m1info”Etudiantnumero: 23456789nom: “Titi”formation: “m2bioinfo”
numeronomformation
12345678‘Toto’‘m1info’
23456789‘Titi’
‘m2bioinfo’






[Not supported by viewer]
etudiant

Identifiants et clés

Identifier chaque instance
par une donnée

(la base de données ne connaît pas les pointeurs)

champ(s) ↔︎ clé primaire

Également utile pour les problématiques de cache

Exemple de définition en Java

package mif04.orm;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity // annotation indiquant une classe
        // à lier à la base de données
public class Etudiant {
  @Id // le champ numéro identifie un étudiant
  private int numero;
  private String nom;
  private String formation;

  // ...
}

Exemple de définition en Python

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class Etudiant(Base):
    __tablename__ = "etudiant"

    numero = Column(Integer, primary_key=true)
    nom = Column(String)
    formation = Column(String)

Côté base de données

(en PostgreSQL)

CREATE TABLE etudiant(
  numero INTEGER PRIMARY KEY,
  nom text,
  formation text
)

Création d’un Entity Manager (Java)

EntityManager em =
  Persistence
    .createEntityManagerFactory("pu-orm")
    .createEntityManager();

À faire une seule fois par contexte d’exécution

Exemple de création d’objet (Java)

    Etudiant e = new Etudiant();
    e.setNom("Toto");
    e.setFormation("m1info");
    e.setNumero(12345678);
    em.getTransaction().begin();
    em.persist(e);
    em.getTransaction().commit();