Lorsqu'on utilise JDBC afin de lire des données en base, le code écrit implique l'exécution d'une requête SQL écrite en dur ainsi que la conversion en un objet Java d'une ligne de la base de données obtenue en réponse dans un objet ResultSet :
ResultSet fetchedArticles = statement.executeQuery("SELECT * FROM articles");
while(fetchedArticles.next()) {
Article art = new Article();
art.setArticleId(fetchedArticles.getInt("id"));
art.setTitre(fetchedArticles.getString("titre"));
art.setCategorie(fetchedArticles.getString("categorie"));
articles.add(art);
}Un ORM (Object-Relational Mapping) vise à éliminer ce genre de code qui se répète pour chaque type d'objet persisté en définissant des correspondances entre les données dans une base et leur représentation sous forme d'objet Java. De même, sans ORM, ajouter une colonne à une table d'une base de données demande de modifier toutes les requêtes SQL de type INSERT / UPDATE.
JPA (Java Persistance API) est une spécification de Java EE décrivant un ORM. Hibernate est une implantation de JPA. Lors des TP, vous pourrez voir que votre projet Spring Boot a été configuré pour utiliser Hibernate en regardant les librairies importées par Maven.
Un contexte de persistance (persistance context) est un ensemble d'entités chargées depuis une base de données ou sauvegardées dans une base de données.
En Hibernate, il est représenté par une instance de org.hibernate.Session. Dans JPA, il s'agit d'une instance de jakarta.persistence.EntityManager Quand Hibernate est utilisé comme implantation de JPA et qu'on utilise un EntityManager dans le code, c'est implicitement un objet Session qui est manipulé.
L'EntityManager associé à un contexte de persistance peut être obtenu en annotant un objet de type EntityManager avec @PersistenceContext.
@PersistenceContext
private EntityManager entityManager;Notez que, par défaut, un contexte de persistance vit (existe) le temps d'une transaction. Sa portée peut être modifiée via un attribut de l'annotation @PersistenceContext.
A chaque entité manipulée dans l'application, on associe un des 4 états suivants :
-
transient : l'entité n'est pas et n'a jamais été attaché à un contexte de persistance. Cette entité n'a pas de correspondance dans la base de données. Cet état correspond à celui d'un objet Java qui vient d'être créé.
-
persistent : l'entité est attachée à un contexte de persistance; après un flush sur l'EntityManager, l'entité correspondante aura une entrée à jour dans la base de données.
-
detached : l'entité a été attachée à un contexte de persistance mais ne l'est plus; c'est le cas lorsque l'instance a été persistée au sein d'une transaction qui s'est achevée;
-
removed : l'entité a été supprimée et disparaîtra effectivement de la base de données au prochain flush.
Quand l'entité est dans l'état persistent, tous les changements effectués sur ses attributs seront répercutés sur l'enregistrement correspondant en base de données au prochain flush qui sera automatiquement déclenché en fin de vie du contexte de persistance ou manuellement par un appel à flush sur l'EntityManager.
On considère la classe ci-dessous définissant des entités :
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
private String name;
// ... getters and setters
}Dans cette section, on discute du comportement des différentes méthodes offertes pour manipuler les entités précédentes dans un contexte de persistance. Ces méthodes requièrent un contexte transactionnel pour s'exécuter lorsque la portée du contexte de persistance est réglé sur "transactionnel".
Cette méthode vise à changer l'état d'une entité de transient à persistent et donc à ajouter cette entité au contexte de persistance.
Person person = new Person();
person.setName("John");
entityManager.persist(person);Au moment où la transaction associée au contexte de persistance s'achèvera ou si un flush est déclenché, une requête SQL de type INSERT est exécutée et provoque l'ajout en base de l'instance de Person.
Notez que le retour de persist est void. La variable person référence l'objet persisté. Quel est l'effet de la méthode sur l'état de l'entité associée au paramètre ?
-
une entité transient devient persistent;
-
si l'entité est déjà persistent alors persist n'a aucun effet;
-
si l'entité est detached alors un appel à persist va lever une exception.
L'intention de cette méthode est de mettre à jour une entité persistent en fixant la valeur de ses attributs en fonction d'une entité detached.
Person person = new Person();
person.setName("John");
entityManager.persist(person);
entityManager.evict(person); // on force l'état "detached"
person.setName("Mary");
Person mergedPerson = (Person) entityManager.merge(person);L'exécution du merge provoque d'abord la recherche d'une entité dont l'id est celui de l'objet en paramètre (cette entité est soit récupérée du contexte de persistance, soit directement de la base de données); la valeur des attributs de l'objet en paramètre du merge est alors copiée dans l'objet retrouvé à l'étape initiale; l'entité mise à jour est retournée.
Dans l'exemple, sont donc manipulés deux objets différents de type Person : celui reçu en paramètre du merge et celui retourné qui appartient au contexte de persistance et qui a été mise à jour. Quel est l'effet de la méthode sur l'état de l'entité associée au paramètre ?
-
si l'entité est detached alors elle est copiée dans une entité persistent.
-
si l'entité est transient alors elle est copiée dans une nouvelle entité persistent;
-
si l'instance est déjà persistent alors merge n'a aucun effet sur son état.
La méthode refresh permet de refraîchir l'état de l'entité reçue en paramètre à partir des informations de la base de données, écrasant ainsi les modifications faites depuis.
Le diagramme suivant représente les différents états JPA possibles pour les entités et l'effet des différentes méthodes sur celui-ci.
Hibernate implante JPA mais fournit aussi des méthodes additionnelles qui ne font pas partie de la spécification : SaveOrUpdate, Update, Save, ... Pour se renseigner sur ces méthodes, on pourra lire le lien suivant :
http://www.baeldung.com/hibernate-save-persist-update-merge-saveorupdate
Notez qu'il est préférable de se contenter de persist, merge et des différentes méthodes décrites dans JPA car cela garantit qu'on pourrait quitter Hibernate et changer d'implantation de JPA sans avoir à revoir profondément son application. On respectera ce principe durant les TPs.
Documentation Hibernate : <http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html_single/>
