A ce stade, voire même dans votre MLD, vous pouvez modifier les noms des attributs et des clefs étrangères pour les rendre plus explicites.
Personne = (#id TINYINT, prenom VARCHAR(50) , nom VARCHAR(50) ); Etudiant = (#id, diplome VARCHAR(45) ); Professeur = (#id, nom VARCHAR(50) ); Matiere = (#id TINYINT, libelle VARCHAR(50) , nombreHeures TINYINT); Cours = (#id TINYINT, heureDebut TIME, heureFin TIME, #id_professeur, #id_matiere); participer = ((#id_etudiant, #id_cours), numeroDePlace INT);
CREATE TABLE Personne( id TINYINT, prenom VARCHAR(50) NOT NULL, nom VARCHAR(50) NOT NULL, PRIMARY KEY(id) )ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE Etudiant( id TINYINT, diplome VARCHAR(45) , PRIMARY KEY(id), FOREIGN KEY(id) REFERENCES Personne(id) )ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE Professeur( id TINYINT, nom VARCHAR(50) , PRIMARY KEY(id), UNIQUE(nom), FOREIGN KEY(id) REFERENCES Personne(id) )ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE Matiere( id TINYINT, libelle VARCHAR(50) , nombreHeures TINYINT, PRIMARY KEY(id) )ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE Cours( id TINYINT, heureDebut TIME, heureFin TIME, id_professeur TINYINT NOT NULL, id_matiere TINYINT NOT NULL, PRIMARY KEY(id), FOREIGN KEY(id_professeur) REFERENCES Professeur(id), FOREIGN KEY(id_matiere) REFERENCES Matiere(id) )ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE participer( id_etudiant TINYINT, id_cours TINYINT, numeroDePlace INT, PRIMARY KEY(id_etudiant, id_cours), FOREIGN KEY(id_etudiant) REFERENCES Etudiant(id), FOREIGN KEY(id_cours) REFERENCES Cours(id) )ENGINE=InnoDB DEFAULT CHARSET=utf8;
Pour la modélisation sous forme de diagramme de classe UML, vous pouvez utiliser :
La classe A hérite de la classe B.
2 solutions (voire 3) :
Uniquement valable dans l’univers objet avec un diagramme de classe mais invalide dans un MCD car incohérent !
2 solutions :
A 1—>* B :
A *—>* B :
@Entity @Table(name="cours") public class Cours implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(unique=true, nullable=false) private byte id; private Time heureDebut; private Time heureFin; //bi-directional many-to-one association to Professeur @ManyToOne @JoinColumn(name="id_1", nullable=false) private Professeur professeur; //bi-directional many-to-one association to Matiere @ManyToOne @JoinColumn(name="id_2", nullable=false) private Matiere matiere; //bi-directional many-to-one association to Participer @OneToMany(mappedBy="cours") private List<Participer> participation;
@Entity @Table(name="etudiant") public class Etudiant implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(unique=true, nullable=false) private byte id; @Column(length=45) private String diplome; //bi-directional one-to-one association to Personne @OneToOne @JoinColumn(name="id", nullable=false, insertable=false, updatable=false) private Personne personne; //bi-directional many-to-one association to Participer @OneToMany(mappedBy="etudiant") private List<Participer> participation;
@Entity @Table(name="personne") public class Personne implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(unique=true, nullable=false) private byte id; @Column(nullable=false, length=50) private String nom; @Column(nullable=false, length=50) private String prenom; //bi-directional one-to-one association to Etudiant @OneToOne(mappedBy="personne") private Etudiant etudiant; //bi-directional one-to-one association to Professeur @OneToOne(mappedBy="personne") private Professeur professeur;
@Entity @Table(name="professeur") public class Professeur implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(unique=true, nullable=false) private byte id; @Column(length=50) private String nom; //bi-directional many-to-one association to Cours @OneToMany(mappedBy="professeur") private List<Cours> cours; //bi-directional one-to-one association to Personne @OneToOne @JoinColumn(name="id", nullable=false, insertable=false, updatable=false) private Personne personne;
@Entity @Table(name="matiere") public class Matiere implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(unique=true, nullable=false) private byte id; @Column(length=50) private String libelle; private byte nombreHeures; //bi-directional many-to-one association to Cours @OneToMany(mappedBy="matiere") private List<Cours> cours;
@Embeddable public class ParticiperPK implements Serializable { //default serial version id, required for serializable classes. private static final long serialVersionUID = 1L; @Column(insertable=false, updatable=false, unique=true, nullable=false) private byte idEtudiant; @Column(name="idCours", insertable=false, updatable=false, unique=true, nullable=false) private byte id1; public ParticiperPK() { } public byte getIdEtudiant() { return this.id; } public void setIdEtudiant(byte id) { this.id = id; } public byte getIdCours() { return this.id1; } public void setIdCours(byte id1) { this.id1 = id1; } public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof ParticiperPK)) { return false; } ParticiperPK castOther = (ParticiperPK)other; return (this.idEtudiant == castOther.idEtudiant) && (this.idCours == castOther.idCours); } public int hashCode() { final int prime = 31; int hash = 17; hash = hash * prime + ((int) this.idEtudiant); hash = hash * prime + ((int) this.idCours); return hash; } }
@Entity @Table(name="participer") public class Participer implements Serializable { private static final long serialVersionUID = 1L; @EmbeddedId private ParticiperPK participerPK; private int numeroDePlace; //bi-directional many-to-one association to Etudiant @ManyToOne @JoinColumn(name="id_etudiant", nullable=false, insertable=false, updatable=false) private Etudiant etudiant; //bi-directional many-to-one association to Cours @ManyToOne @JoinColumn(name="id_cours", nullable=false, insertable=false, updatable=false) private Cours cours;
Les clefs et les index permettent un accès rapide aux données et servent aussi de contrainte d’intégrité.
La clef primaire est constituée d’un id. Choisir un identifiant unique et naturel plutôt qu’un entier auto-incrémenté qui est unique mais n’empêche pas la création de doublons sauf si vous déclarez d’autres champs comme “unique”. Le problème est que si l’identifiant natuel est de type chaîne de caractères, les performances sont amoindries par rapport à l’utilisation d’une clef de type numérique.
Les auto-incrément de clef primaire simplifie grandement les relations entre les tables mais ceci est discutable (Il a l’inconvénient de ne pas contrôler les doublons).
Personnellemment, je ne suis pas partisan des clefs auto-généréés ! Ce n’est pas une bonne pratique si vous avez la possibilité de trouver une clef naturelle ou du moins qui a un sens !
Raisons :
Il s’agit d’un clef dite id technique qui ne doit pas être visible pour l’utilisateur !
Même si la clef primaire n’est pas obligatoire en MYSQL, vous devez en déclarer une obligatoirement.
C’est une erreur de conception d’avoir une table sans clef primaire ! Malheureusement, ça existe même dans les bases de données des ESN !
La *clef fonctionnelle (ou clef unique) est l’ensemble des champs constituants l’unicité d’un enregistrement (id primaire exclu).
L’ajout de cette clef vous oblige à déterminer l’unicité des enregistrements et contribue fortement à l’intégrité des enregistrements de la base de données.
Pour rappel : l’id est un concept technique, il ne doit pas être vu par l’utilisateur.
C’est la clef fonctionnelle qui compte pour un utilisateur.
Cette clef est appelée aussi clef unique. Elle est composée d’un ou plusieurs champs.
S’il y a un seul champ, on la définit grâce à une contrainte d’unicité sur le champ.
Prenons l’exemple d’un produit qui a un champ reference. Il est unique, il ne peut y avoir 2 produits qui ont la même référence !
CREATE UNIQUE INDEX `reference_UNIQUE` ON `stock`.`produit` (`reference` ASC);
S’il y a plusieurs champs, on la définit grâce à un index unique composé des champs la composant.
Prenons l’exemple d’une personne qui a 4 champs : nom, prenom, dateDeNaissance, email. les nom, prenom, dateDeNaissance constitue la clef fonctionnelle.
Il ne peut pas avoir 2 personnes avec le même triplet nom, prénom et date de naissance, quoique…
CREATE UNIQUE INDEX `uk_UNIQUE` ON `carnet`.`personne` (`nom` ASC, `prenom` ASC, `dateDeNaissance` ASC);
Une clé fonctionnelle ou unique (ou “business key” en anglais) est une clé qui a une signification sémantique ou fonctionnelle dans le contexte de l’application. Elle permet d’identifier de manière unique une entité en se basant sur des caractéristiques fonctionnelles. Par exemple, dans une application de gestion de commandes, la combinaison du numéro de commande et de la date de commande peut être considérée comme une clé fonctionnelle unique, car elle identifie de manière unique une commande.
Une clé technique est une clé qui est générée par le système et qui n’a pas de signification sémantique ou fonctionnelle pour les utilisateurs. Elle est souvent utilisée en interne par le système pour faciliter la gestion des entités. Par exemple, dans une base de données, une clé technique peut être un identifiant numérique généré automatiquement pour chaque enregistrement.
La principale différence entre les 2 types de clés est leur signification et leur utilisation :
Dans la logique des choses, une fois l’enregistrement créé, on ne devrait pas pouvoir modifier les valeurs de la clef fonctionnelle. Ce contrôle est du domaine du développement (java, php). Imaginez : vous créez dans la base un produit avec une référence. Vous faites une commande avec ce produit et éditez une facture. Vous changer ensuite la référence du produit. Il sera impossible à l’acheteur de retrouver son produit inscrit sur la facture.
La clef étrangère permet d’établir la relation entre les 2 tables et maintient en partie l’intégrité de la base. C’est une contrainte de base. Cette clef vérifie si l’enregistrement étranger existe. Ceci est très utile lors de la création et la suppression d’un enregistrement Dans une relation 1.., c’est la table proche de * qui contient la clef étrangère. Dans une relation *.., on décompose en 2 relations 1..*. Dans une relation 1..1, on décide d’une table maître.
Les index permettent une lecture plus rapide de la base en fonction des champs indexés. En théorie, on pourrait avoir autant d’index que de critères de recherche. Cependant les index sont coûteux en place et en temps d’écriture. De plus les processeurs et les moteurs de base de données sont devenus très performants. En fait tout dépend de l’utilisation :
Il existe 6 niveaux de forme normale. Une base de données relationnelle doit être au minimum en 3ème forme normale. Avec les id techniques, il est très facile de ne pas respecter la normalisation. Une base relationnelle non normalisée entraîne de graves erreurs de comportement, et oblige le développeur à écrire plus de code.
Dans une base de données relationnelle, il y a 3 règles obligatoires à connaître.
Il n’y a pas de tableau, ni de liste dans un champ. Par exemple, il est déconseillé de mettre un nom suivi du prénom dans le même champ. On doit dans ce cas créer un champ nom et un champ prénom. De même, il est interdit de mettre un tableau (array) de noms d’article dans la table facture. On doit créer une table article contenant le nom de l’article.
Les informations sur une entité sont sur un seul enregistrement de la table. Il est interdit de disperser les informations sur plusieurs enregistrements.
L’information est représentée une seule fois dans la base. Il est interdit d’avoir des doublons dans la table. Pour cela il faut ajouter une contrainte d’unicité (clef unique ou fonctionnelle). De même il est interdit de dupliquer l’information dans une autre table. Par exemple la description d’un fournisseur ne peut pas être dans une table produit, seul y figure son id et des caractéristiques propres au produit.
Pour le fun, voici la définition mathématique. Note : la définition a été écrite avant l’utilisation systématique des id, il faut comprendre par clef primaire la clef fonctionnelle.
Une relation est en 1ère forme normale si elle ne contient que des “valeurs atomiques”, c’est-à-dire pas de “groupes répétitifs”. Non décomposable Un champ contient une valeur au plus : pas de tableau, ni de liste.
Une relation est en 2ème forme normale si elle est déjà en 1ère forme normale, et si tout attribut n’appartenant pas à la clé (primaire) dépend complètement de cette clef. Un champ non clé primaire ne doit pas dépendre d’une partie de la clé primaire. Il doit en dépendre entièrement.
Un champ non clé primaire ne doit pas dépendre d’un autre champ non clé primaire. Dans ce cas, on peut décomposer la table en deux tables afin d’éviter une redondance d’informations dans la base.
Vous voulez des explications complètes, lisez ceci. STOP, je n’ai rien compris, lisez ceci.
Déclarer les VARCHAR avec des longueurs aléatoires. Il faut bien réfléchir à la longueur que prend un champ texte.
Rien de compliqué, juste du bon sens et de la pratique…