Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. JPA et Java Hibernate
  3. Manipulation des données
Extrait - JPA et Java Hibernate Apprenez le mapping objet-relationnel (ORM) avec Java
Extraits du livre
JPA et Java Hibernate Apprenez le mapping objet-relationnel (ORM) avec Java Revenir à la page d'achat du livre

Manipulation des données

Préparation

Afin de continuer sur ce chapitre, il faut avoir à disposition le fichier persistence.xml et les différentes entités créées dans le projet commencé à la section Premier lancement de NetBeans - Création du projet du chapitre Environnement de développement et continué tout au long du chapitre Préparation d’un projet. Pour cela, le fichier persistence.xml et les entités sont à remplacer par ceux trouvés dans l’exemple PROJET_ENI_MAVEN.zip téléchargeable depuis la page Informations générales.

Une fois les fichiers mis en place, il faut modifier les informations de connexion dans le fichier persistence.xml pour correspondre à vos paramètres de connexion à la base de données comme expliqué à la section Paramétrage de l’ORM - Les propriétés du fichier de persistance du chapitre Préparation d’un projet :


<property name="javax.persistence.jdbc.url" value="..."/> 
<property name="javax.persistence.jdbc.user" value="..."/> 
<property name="javax.persistence.jdbc.driver" value="..."/> 
<property name="javax.persistence.jdbc.password" value="..."/>
 

Afin de voir les requêtes envoyées par l’ORM au système de gestion...

Établissement de la connexion

Maintenant que le mapping des données a été réalisé, il n’y a plus qu’à se connecter à la base de données pour pouvoir les manipuler. C’est-à-dire les créer, les lire, les modifier ou les supprimer. Pour cela, il a été vu au chapitre Concept des ORM, que l’application doit obligatoirement interroger les données via un EntityManager, que cet EntityManager doit être fourni par un EntityManagerFactory qui lui-même connaît le mapping objet-relationnel (l’unité de persistance).

1. EntityManagerFactory

Pour créer l’EntityManagerFactory, il faut le nom de l’unité de persistance qui se trouve dans le fichier persistence.xml.

Par exemple, dans le fichier fourni, le nom est projetEni et se trouve à la ligne suivante :


<persistence-unit name="projetEni" transaction-type="RESOURCE_LOCAL">
 

L’EntityManagerFactory ne peut pas être instancié simplement car cela voudrait dire qu’il faut instancier l’EntityManagerFactory de l’implémentation de JPA, ce qui équivaudrait à cloisonner l’application et perdre le niveau d’abstraction que JPA permet.

Il a été vu que l’unité de persistance peut être paramétrée soit en mode RESOURCE_LOCAL soit en mode JTA. En mode JTA, l’unité de persistance est gérée par le conteneur JEE, il n’y a donc pas d’EntityManagerFactory à paramétrer au niveau du code source. En mode RESOURCE_LOCAL, elle doit être gérée par l’application. Deux solutions sont possibles selon s’il y a un conteneur JEE ou non.

a. Avec conteneur JEE, en RESOURCE_LOCAL

Lorsqu’un conteneur JEE est disponible, il est recommandé de laisser le serveur gérer l’EntityManager. Pour cela, il faut l’injecter dans le code source en utilisant l’annotation @PersistenceUnit juste avant la déclaration de l’EntityManagerFactory.

L’exemple ci-dessous montre comment configurer l’EntityManagerFactory avec l’unité de persistance "projetEni" :


@PersistenceUnit(unitName = "projetEni") 
private EntityManagerFactory emf;
 

Cette utilisation de JPA, qui demande...

Les transactions

Afin d’assurer la cohérence des données, les systèmes de gestion de base de données ont introduit le concept de transaction. Naturellement, JPA reprend ce concept. Ainsi, comme vu précédemment, un EntityManager est associé à une seule transaction active à un moment donné. C’est-à-dire qu’avec une seule instance d’EntityManager, une suite de transactions peut être exécutée mais deux transactions ne peuvent pas être actives en parallèle. Pour rappel, une transaction est active entre son début (begin) et sa fin (commit ou rollback).

Ainsi, JPA met en place l’interface EntityTransaction qui permet d’uniformiser leur utilisation et de s’abstraire de l’implémentation JPA utilisée. Cette transaction se récupère avec la méthode getTransaction() de l’EntityManager


EntityManager em = JpaUtil.getEmf().createEntityManager(); 
EntityTransaction tr = em.getTransaction();
 

Une des bonnes pratiques de JPA est d’encapsuler toutes les opérations effectuées sur la base de données dans une transaction. En effet, suivant certaines bases de données et implémentations, seules les opérations de lecture peuvent ne pas être transactionnelles, ce qui est le cas avec MySQL et Hibernate.

1. Exécution d’une transaction

Comme en SQL, une transaction commence avec un begin et se termine soit par un commit, soit par un rollback. Ainsi, l’interface...

Création d’une entité

Afin de remplir la base de données, il faut utiliser des requêtes insert avec le langage SQL. Avec les ORM, il suffit d’instancier un nouvel objet et de le persister. L’implémentation utilisée se charge de créer la requête d’insertion.

1. Création d’une entité simple

La création d’un objet simple correspond à l’ajout d’un seul tuple à la base de données. Pour ce faire, il suffit d’instancier une entité, de renseigner ses propriétés et d’utiliser la méthode persist() de l’EntityManager.

L’exemple suivant permet de créer une nouvelle Personne ayant pour nom « DUPONT », pour prénom « Pierre » et comme date de naissance le « 20/05/1980 » :


EntityManager em = ...; 
  
//Création de la personne 
Personne p = new Personne(); 
p.setNom("DUPONT"); 
p.setPrenom("Pierre"); 
Calendar dateNaiss = Calendar.getInstance(); 
dateNaiss.set(1980, 5, 20); 
p.setDateNaissance(dateNaiss.getTime()); 
System.out.println("1-" + p.getId()); 
  
//Sauvegarde de la personne 
em.persist(p); 
System.out.println("2-" + p.getId());
 

Ce qui donne dans la console :


1-null 
2-null
 

Il apparaît donc que l’id n’a pas été initialisé bien que le champ soit en auto-incrément, et en faisant une requête sur la base de données, le tuple n’est pas ajouté. Cela vient du fait qu’il y a une sécurité avec JPA/Hibernate, qui empêche les modifications de la base de données si elles ne sont pas encapsulées dans une transaction.

En démarrant une transaction pour y exécuter les modifications, le code source donne :


EntityManager em = ...; 
  
//Création de la personne 
Personne p = new Personne(); 
p.setNom("DUPONT"); 
p.setPrenom("Pierre"); 
Calendar dateNaiss = Calendar.getInstance(); 
dateNaiss.set(1980, 5, 20); 
p.setDateNaissance(dateNaiss.getTime()); 
System.out.println("1-" + p.getId()); 
  
//Début de la transaction 
EntityTransaction tr = em.getTransaction(); ...

Récupération d’une entité

Afin de récupérer les données en base de données, la méthode habituelle est d’exécuter une requête de sélection avec différentes colonnes et différents critères de recherche. Avec JPA, la méthode généralisée pour récupérer une entité est de récupérer toutes les colonnes mappées et le critère de recherche est la clé primaire car elle assure l’unicité de l’entité en base de données. Cette récupération s’effectue depuis une instance d’EntityManager avec la méthode find(). L’exécution de cette méthode n’est pas nécessairement encapsulée dans une transaction, mais il est recommandé de le faire si les données récupérées peuvent être modifiées par la suite.

1. Entité avec une clé primaire simple

La récupération d’une entité grâce à sa clé primaire simple s’effectue simplement en exécutant la méthode find() de l’EntityManager, en donnant comme paramètres la classe de l’entité et la valeur de la clé primaire.

Par exemple, pour récupérer l’entité Personne ayant pour clé primaire la valeur 1 :


em.getTransaction().begin(); 
 
Personne p = em.find(Personne.class, 1); 
System.out.println(p.getId()+" "+p.getNom()+" "+p.getPrenom()+"  
"+p.getDateNaissance()); 
 
em.getTransaction().rollback();
 

L’exécution de ce code donne dans la console :


1 DUPONT Pierre 1980-05-20
 

L’explication de la requête envoyée par l’ORM est formulée à la section Depuis une entité déjà chargée.

Le type du paramètre de la méthode est important, il doit correspondre au type de la clé primaire de l’entité.

Par exemple, si on passe la chaîne "1" au lieu de l’entier 1 à la méthode find (), une exception est levée :


em.getTransaction().begin(); 
 
Personne p = em.find(Personne.class, "1"); 
System.out.println(p.getId()+" "+p.getNom()+"...

Suppression d’une entité

Afin de maintenir à jour une base de données, certaines données sont à supprimer car elles deviennent obsolètes. Pour ce faire, en SQL, il faut utiliser la commande DELETE. Avec JPA, il faut utiliser la méthode remove() de l’EntityManager. Comme toute action de modification sur la base de données, l’action de suppression doit être encapsulée dans une transaction.

La suppression ne fonctionne que sur une entité managée, c’est-à-dire que la méthode remove() n’accepte des entités qu’après leur persist() ou récupérées via la méthode find(), un getter ou une requête.

Par exemple, l’enchaînement suivant génère une exception :


Personne p = new Personne(1); 
em.remove(p);
 

1. Suppression simple

La suppression simple est la suppression d’une entité, c’est-à-dire qu’il faut supprimer uniquement le tuple voulu.

L’exemple suivant montre comment supprimer une entité Personne créée lors d’une transaction précédente :


//Transaction pour créer la personne 
em.getTransaction().begin(); 
Personne p = new Personne(); 
em.persist(p); 
int idPersASupp = p.getId(); 
em.getTransaction().commit(); 
 
//Transaction pour supprimer la personne 
em.getTransaction().begin(); 
Personne pASupp = em.find(Personne.class, idPersASupp); 
em.remove(pASupp); 
em.getTransaction().commit();
 

2. Suppression en cascade

Lors de la suppression d’une entité, un point important à prendre en compte est la relation de cette entité avec les autres, et si le mode de suppression en cascade est activé. Ce mode cascade se trouve dans le mapping de l’entité sur la relation (@OneToOne, @OneToMany, @ManyToOne et @ManyToMany) via la propriété...

Modification d’une entité

Après la création, la lecture et la suppression des données, une phase importante est la modification. En langage SQL, cette modification s’effectue via un update ; avec les ORM, elle se fait simplement en modifiant la propriété voulue d’une entité managée puis en validant la transaction de l’EntityManager. Cette modification s’effectue donc en appelant la fonction merge() de l’EntityManager attendant l’entité en paramètre, le tout encapsulé dans une transaction.

1. Modification des champs d’une entité

Une entité est managée si elle est dans le contexte de persistance, c’est-à-dire si elle est récupérée depuis l’EntityManager (avec une requête, la méthode find() ou son getter) ou si elle vient d’être créée en base (avec la méthode persist()). Si elle vient d’être instanciée avec son constructeur, alors elle n’est pas considérée comme managée.

a. Depuis une entité managée

Afin de modifier des données, la première méthode est de récupérer l’entité managée correspondante, modifier la valeur puis valider.

Par exemple, pour modifier le nom de l’entité Personne ayant l’id 1, il faut démarrer une transaction, utiliser la méthode find() afin de récupérer une entité managée, modifier le nom via le setter pour mettre à jour le contexte de persistance puis valider la transaction.


em.getTransaction().begin(); 
Personne p1 = em.find(Personne.class, 1); 
p1.setNom("nouveauNom"); 
em.getTransaction().commit(); 
em.close();
 

Ainsi, dans la console, la requête de modification apparaît bien :


Hibernate: update personne set date_naissance=?, nom=?, prenom=?  
where id=?
 

b. Depuis une entité non managée

La deuxième méthode de modification d’une donnée est de partir d’une entité non managée. Cette utilisation est possible mais est beaucoup plus contraignante. Il faut créer l’entité voulue avec la bonne clé primaire, initialiser tous les champs avec les valeurs voulues, utiliser la fonction merge()...

Synchronisation

1. De l’ORM vers la base de données

Lors des sessions longues, il peut être intéressant de valider le contexte de persistance en cours vers la base de données sans valider la transaction. Pour cela, il faut utiliser la méthode flush() de l’EntityManager.

L’exemple suivant montre à quel moment les requêtes sont effectuées :


em.getTransaction().begin(); 
Personne p = new Personne(); 
System.out.println("persist"); 
em.persist(p); 
System.out.println("fin du persist"); 
p.setNom("nouveau2"); 
p.setPrenom("test1"); 
System.out.println("premier flush"); 
em.flush(); 
System.out.println("fin du premier flush"); 
p.setPrenom("test11"); 
p.setPrenom("test12"); 
p.setPrenom("test13"); 
System.out.println("deuxieme flush"); 
em.flush(); 
System.out.println("fin du deuxieme flush"); 
p.setPrenom("test2"); 
p.setPrenom("test21"); 
p.setPrenom("test22"); 
System.out.println("commit"); 
em.getTransaction().commit(); 
System.out.println("fin du Commit");
 

Ainsi, le résultat ci-dessous apparaît dans la console :


persist 
Hibernate: insert into personne (date_naissance, nom, prenom)  
values (?, ?, ?) 
fin du persist ...