Vous êtes sur la page 1sur 14

Faculté I&C, Claude Petitpierre 1 Faculté I&C, Claude Petitpierre 2

Objet JPA
@Entity
public class Vin implements java.io.Serializable {

JPA: Java Persistence API @Id


@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id = null;
private String nomVin;

• Javabeans synchronisables public Long getId() { // getters – setters, générés par Eclipse
avec la base de données return id;
}
• Autres noms: Hibernate, EJB3 public void setId(Long id) {
this.id = id;
}
public String getNomVin() {
return nomVin;
}
}

Faculté I&C, Claude Petitpierre 3 Faculté I&C, Claude Petitpierre 4

Objet JPA avec attributs non persistants


@Entity Importations dans un JPA
public class Vin implements java.io.Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) import javax.persistence.Entity;
private Long id = null; import javax.persistence.GeneratedValue;
private String nomVin;
import javax.persistence.GenerationType;
transient private String flag; // pas dans la BD, pas sérialisé import javax.persistence.Id;
@Transient
private String caracteristique; // pas dans la BD @Entity
public class Vin implements java.io.Serializable {
// getters / setters
} ...
}
// les transients ne sont utilisés que dans la JSF, pas dans la BD
Faculté I&C, Claude Petitpierre 5 Faculté I&C, Claude Petitpierre 6

Insérer un objet dans la base de données Manager.java est lié à un thread


(détails)
javax.persistence.EntityManager em = ejb3_utility.Manager.open();
javax.persistence.EntityTransaction tx = em.getTransaction(); private static ThreadLocal<Manager> threadManager
tx.begin(); = new ThreadLocal<Manager>() { // nameless class

Vin vin = new Vin(nomV, cepage, annee); protected synchronized Manager initialValue() {
em.persist(vin); return (new Manager());
tx.commit(); // or tx.rollback(); }
ejb3_utility.Manager.close(); };

Faculté I&C, Claude Petitpierre 7 Faculté I&C, Claude Petitpierre 8

Manager.java (partiel)
private EntityManager em = null;
public static EntityManager open() { Création d’un projet contenant
Manager manager = (Manager) threadManager.get();
if (manager.em == null) {
des JPAs
Manager.emf = Persistence
.createEntityManagerFactory("NomDeManager");
manager.em = Manager.emf.createEntityManager();
manager.em.setFlushMode(FlushModeType.COMMIT);
}
return manager.em;
}
Faculté I&C, Claude Petitpierre 9 Faculté I&C, Claude Petitpierre 10

Projet avec objets JPA


Persistence.xml (partie)
• Créer un Dynamic Web Project
<persistence-unit name="NomDeManager">
• Ajouter la liste de .jar dans <class>db.Vin</class>
WebContent/WEB-INF/lib <properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
• Ajouter ejb3_utility/Manager.java dans src <property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
• Ajouter META-INF/persistence.xml dans le <property name="hibernate.connection.username" value="username"/>
<property name="hibernate.connection.password" value="password"/>
répertoire src et introduire le nom de chaque <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test"/>
JPA dans ce fichier <property name="javax.persistence.transactionType" value="RESOURCE_LOCAL"/>
</properties>
• Ajouter mysql_connector.jar dans Tomcat/lib </persistence-unit>
</persistence>

Faculté I&C, Claude Petitpierre 11 Faculté I&C, Claude Petitpierre

Introduire un objet
dans la base de données
Intégration d'un JPA dans un projet
javax.persistence.EntityManager em = ejb3_utility.Manager.open();
javax.persistence.EntityTransaction tx = em.getTransaction();
• Structure selon Usager.java tx.begin();
Vin vin = new Vin(nomV, cepage, annee);
• Introduire le JPA dans les ManagedBean, s'il est em.persist(vin);
affiché dans une JSF // les modifications faites ici sont aussi introduites dans la BD
• Introduire son package.nom dans persistence.xml tx.commit(); // or tx.rollback();
ejb3_utility.Manager.close();

// persist ajoute automatiquement un id dans le champ @Id de l'objet


Faculté I&C, Claude Petitpierre Faculté I&C, Claude Petitpierre

Opération merge
Modification d’un objet dans la BD: merge()
Manager
p = new Personne() Deuxième
• A la fin d'une méthode, avant de retourner à la page transaction
à afficher dans le navigateur, on doit naturellement
Personne Personne
fermer la transaction. Personne
p
• Lorsque l'on affiche un objet sur une page, on n'est p1
donc plus dans la transaction
• Lorsque l’utilisateur demande à la page de
em.persist(p)
transmettre les modifications (<form action=" ">), elle Première p1 = em.merge(p)
appelle une nouvelle action transaction
• Dans cette action, il faut ouvrir une nouvelle
DB
transaction et y réintroduire l'objet Æ merge() pour
que les modifications soient enregistrées dans la BD
// les modifications de p1 sont transmises dans la BD

Faculté I&C, Claude Petitpierre Faculté I&C, Claude Petitpierre

Fonctionnement du merge Opération merge

Manager Page Page Page Page

5
Personne Personne Personne
id=3 Personne Personne
session id=3 id=3
Personne Personne
p1
p Id : 5 Id : 5

em.persist(p) p = em.merge(p) p = em.merge(p)


p1 = em.merge(p) p1 est une copie de p
A programmer
Seules les modifications
explicitement
sur p1 seront transmises DB
à la base de données à la
fermeture de la transaction
Faculté I&C, Claude Petitpierre Faculté I&C, Claude Petitpierre

merge d'un objet déjà dans la transaction merge d'un objet déjà dans la transaction

Manager Manager
5 Personne
p2
5 Id : 5
mémorisation
mémorisation p1 Personne p3 copie du contenu
p1 Personne p3 Personne
Id : 5 p4
Personne
Id : 5 Id : 5
Id : 5
p4 = em.merge(p2)
p3 = em.merge(p1)
p3 = em.merge(p1)
On suppose que p1 et p2 ont été créés par un moyen quelconque, par exemple
On suppose que p1 et p2 ont été créés par un moyen quelconque, par exemple en introduisant directement 5 dans l' id. Le premier merge introduit p3 dans la
en introduisant directement 5 dans l'id transaction
Si l'id est déjà référencé, le contenu du nouvel objet est copié dans l'objet Comme l' id de p2 est déjà référencé, le deuxième merge copie le contenu du
référencé et l'objet référencé est retourné nouvel objet dans l'objet p3 puis la référence de cet objet est retournée dans p4

Faculté I&C, Claude Petitpierre Faculté I&C, Claude Petitpierre

Indirection dans la session


(p reste dans la session)
Relations entre JPAs
session

Business
Personne p; Personne
public void action() {
...
p = em.merge(p);
...
}
Faculté I&C, Claude Petitpierre 21 Faculté I&C, Claude Petitpierre 22

Relations entre JPA 1:1 Relations entre JPA 1:N


Marchand Vin Marchand Vin
1 1 1 N
nomMarchand nomVin nomMarchand nomVin
vin cepage vins cepage
annee annee
marchand marchand

Dans Marchand
(il faut choisir un @OneToOne(mappedBy="marchand") @OneToMany(mappedBy="marchand")
Dans Marchand
côté pour mappedBy) private Vin vin; private Collection<Vin> vins;

@OneToOne Dans Vin @ManyToOne Dans Vin


private Marchand marchand; private Marchand marchand;

// Getters – setters pour les deux champs // Getters – setters pour la collection et pour le champ marchand

Faculté I&C, Claude Petitpierre 23 Faculté I&C, Claude Petitpierre 24

Relations entre JPA N:M


Relations entre JPA N:M, cascade
Marchand Vin
N M
nomMarchand nomVin
Dans Marchand
vins cepage
annee
marchands
@ManyToMany (cascade = CascadeType.ALL)

@ManyToMany private Collection<Vin> vins = new ArrayList<Vin>();


Dans Marchand
private Collection<Vin> vins;
@ManyToMany(mappedBy="vins")
private Collection marchands = new ArrayList<Marchand>();
@ManyToMany(mappedBy="vins") Dans Vin
private Collection marchands; Dans Vin

// Getters – setters pour les deux collections // Persistence et remove cascadés


Faculté I&C, Claude Petitpierre 25 Faculté I&C, Claude Petitpierre

Relation dans la base de données et


dans les objets
C'est la responsabilité du développeur d'introduire les relations
Insertion de JPAs dans les relations dans les deux sens entre les objets.

Pour qu'elles soient introduites dans la base de données, il faut


faire cette opération dans une transaction du manager. C'est-à-
dire que le principal soit attaché (voir plus loin) et l'objet ajouté
pas libre.

De plus il faut introduire au moins l'élément du côté qui n'a pas


l'indication mappedBy.

Faculté I&C, Claude Petitpierre 27 Faculté I&C, Claude Petitpierre 28

Relations entre JPA 1:1 Relations entre JPA 1:N


@OneToMany(mappedBy="marchand")
@OneToOne(mappedBy="marchand")
private Collection<Vin> vins;
private Vin vin;

@ManyToOne
@OneToOne
private Marchand marchand;
private Marchand marchand;

tx.begin(); tx.begin(); tx.begin(); tx.begin();


marchand = em.merge(marchand); vin = em.merge(vin); marchand = em.merge(marchand); vin = em.merge(vin);
em.persist(v); em.persist(m); em.persist(v); em.persist(m);
marchand.setVin(v); vin.setMarchand(m); marchand.vins.add(v); vin.setMarchand(m);
tx.commit(); tx.commit(); tx.commit(); tx.commit();

// nécessaire et suffisant pour // nécessaire et suffisant pour


// introduire la relation dans la BD // introduire la relation dans la BD
Faculté I&C, Claude Petitpierre 29 Faculté I&C, Claude Petitpierre

Relations entre JPA N:M


Parcourir des éléments d'une relation x:N
@ManyToMany
private Collection<Vin> vins; public class Marchand {
@OneToMany(mappedBy="marchand")
private Collection<Vin> vins;
@ManyToMany(mappedBy="vins") public void setVins(…) { … }
public Collection<Vin> getVins(…) { … }
private Collection marchands; }

tx.begin(); tx.begin(); // . . . transaction . . .


marchand = em.merge(marchand); vin = em.merge(vin); marchand = em.merge(marchand);
em.persist(v); em.persist(m); for (Vin v: marchand.getVins()) { // relation
marchand.vins.add(v); vin.marchands.add(m); System.out.println(v.getNom());
tx.commit(); tx.commit(); }

// les instructions d'un seul côté suffisent // Les vins sont automatiquement "mergés" quand
// pour entrer la relation dans la BD // on parcourt la collection

Faculté I&C, Claude Petitpierre Faculté I&C, Claude Petitpierre

merge dans les relations merge dans les relations


et
c Cours c Cours
id=3 id=3
Etudiant Etudiant Etudiant Etudiant Etudiant Etudiant
id=8 id=4 id=7 id=8 id=4 id=7

c1 = em.merge(c) c1 = em.merge(c)
et = em.merge(et)
c1 Cours c1 Cours et Etudiant
Manager id=3 pas de copie Manager
id=3 id=4
id: 3, 8, 4, 7 3, 8, 4, 7

for (Vin v: marchand.getVins()) { for (Vin v: marchand.getVins()) {


System.out.println(v.getNom()); System.out.println(v.getNom());
} // mergés automatiquement } // le 4 est repris car il est déjà mergé

c1 Cours c1 Cours
BD id=3 Les modifications sur id=3
Etudiant Etudiant Etudiant Etudiant Etudiant
id=8 id=4 id=7 les objets bleus sont id=8 id=7
extraction répercutées dans la BD
Faculté I&C, Claude Petitpierre Faculté I&C, Claude Petitpierre

Rattachement à la base de données em = Manager.open(); Elements


client = new Client("Hans"); attachés
Comme décrit précédemment, les objets JPA sont créés // libre
indépendamment de la base de données et leur synchroni-
sation avec celle-ci est faite de façon explicite. tx.begin();
em.persist(p);
Un objet peut donc être libre (pas d'ID), attaché (un ID et
dans une transaction) ou détaché (a conservé son ID, mais // attaché
n'est pas lié à une transaction). tx.commit();
Manager.close();
Un objet mémorisé dans la session HTTP peut garder son ID
d'une page à l'autre. Pour le réintégrer dans une transaction, // détaché, son id reste initialisé
il faut utiliser merge, comme expliqué sur les pages qui client.setId(0);
précèdent.
// libre
De même pour un objet qui serait lu par une query, il obtient client = (Client) result.getSingleResult();
un ID, mais il n'est pas attaché à la transaction.
// détaché

Faculté I&C, Claude Petitpierre Faculté I&C, Claude Petitpierre

merge()
Client client = (Client)result.getSingleResult();
Mises à jour
// détaché, son id est initialisé
tx.begin(); Quand un objet attaché est mis à jour, ses modifica-
client2 = em.merge(client); tions sont suivies dans le manager et lors du commit,
// attaché, les modifications seront enregistrées au commit elles sont transmise dans la base de données.
tx.commit(); Il en est de même avec les relations.
Pour vérifier que le système réagit selon nos plans, il
// em.merge() retourne une copie intégrée dans la transaction suffit d'afficher les tables en utilisant le moniteur de
// Donc attention, si client est enregistré dans la session MySQL
// HTTP, il faudra le ré-enregistrer dans cette session !
Faculté I&C, Claude Petitpierre 37 Faculté I&C, Claude Petitpierre 38

Utilisation de JQL
JQL Query result = em.createQuery("SELECT v FROM Vin v");
vin = (Vin)result.getSingleResult(); // une seule ligne,
// une exception est jetée si la commande
Adaptation de SQL aux JPA
// en définit plusieurs

vins = (ArrayList<Vin>) result.getResultList(); // plusieurs lignes

// le find est automatiquement attaché au manager

Faculté I&C, Claude Petitpierre Faculté I&C, Claude Petitpierre 40

JQL avec relations


Elimination d'un élément Relation 1:1
SELECT m FROM Marchand m

Query result = em.createQuery( WHERE m.vin.nom='Epesse‘


"SELECT v FROM Vin v WHERE v.nomVin="Bourgogne" (m.vin est un attribut)
);
vin = (Vin)result.getSingleResult();
Relation 1:N, N:N
...
SELECT m FROM Marchand m, IN(m.sesVins) v
em.remove(vin);
WHERE v.nom='Epesse'
(m.sesVins est une collection)
Faculté I&C, Claude Petitpierre Faculté I&C, Claude Petitpierre 42

Introduire un élément dans une relation Lazy loading (1)


Lorsque le manager charge une relation qui ne nécessite qu’un attribut
Query result = em.createQuery( (1:1, 1:N), il charge directement l’unique élément de la relation.
"SELECT c FROM Client c Lorsqu’il doit charger une liste (N:M, N:1), il ne charge les éléments que
WHERE c.nom=' "+nomClient+" ' "); lorsqu’ils sont accédés. Ainsi, dans l’exemple ci-desous, les éléments ne
sont plus accessibles, car la transaction est fermée avant d’accéder aux
Client client = (Client) result.getSingleResult(); éléments (on suppose que client a une relation 1:N avec produits):

Client client = (Client)result.getSingleResult();


tx.begin(); tx.begin();
client.getPanier().add(p); client2 = em.merge(client);
tx.commit();
tx.commit(); // or tx.rollback(); for (Produit p: client2.listeProduits) {
print(p); // ne sont plus accessibles
}

Faculté I&C, Claude Petitpierre 43 Faculté I&C, Claude Petitpierre

Lazy loading (2) Transactions gérées par version


Soit la séquence d'événements:
Pour forcer le chargement pendant que la liste est dans la transaction, il faut
y accéder, par exemple en appelant la fonction size(): • On crée ou lit un JPA depuis une page
• Dans un page suivante, on merge ce JPA pour le modifier
Client client = (Client)result.getSingleResult(); La modification est faite correctement
tx.begin();
Client client2 = em.merge(client);
• On relit une JPA depuis une page
client2.size(); // juste pour forcer le chargement
tx.commit(); • Un autre utilisateur lit la même JPA
for (Produit p: client2.listeProduits) { • Il la modifie (La modification est faite correctement)
print(p); // disponibles
• Le premier utilisateur clique sur sa page
}
• Il modifie le JPA
// Note: on pourrait aussi spécifier que le chargement soit fait d’office La première modification est oubliée.
// (eager loading) dans les paramètres du JPA (voir la doc spécialisée).
Le champ de version va permettre de signaler ce problème.
Faculté I&C, Claude Petitpierre Faculté I&C, Claude Petitpierre

Transactions gérées par version


Transactions gérées par version public void enregistrer() { // création d'un JPA
this.id = null;
try {
javax.persistence.EntityManager em = ejb3_utility.Manager.open();
@Version javax.persistence.EntityTransaction tx = em.getTransaction();
tx.begin();
private int uneVersion; em.persist(this);
tx.commit(); // or tx.rollback();
// getters / setters } finally {
ejb3_utility.Manager.close();
}
} // rien de particulier

Faculté I&C, Claude Petitpierre Faculté I&C, Claude Petitpierre

Transactions gérées par version Transactions gérées par version


public void modifier() {
public void modifier() {
try {
try {
javax.persistence.EntityManager em = ejb3_utility.Manager.open();
javax.persistence.EntityManager em = ejb3_utility.Manager.open();
javax.persistence.EntityTransaction tx = em.getTransaction();
javax.persistence.EntityTransaction tx = em.getTransaction();
tx.begin();
tx.begin();
Produit2 p = em.merge(this); // reprend la version de l’objet pas celle
Produit2 p = em.merge(this);
p.prix ++; // de la BD
p.prix ++;
tx.commit(); // or tx.rollback()
tx.commit(); // or tx.rollback()
} finally {
} finally {
ejb3_utility.Manager.close();
ejb3_utility.Manager.close();
}
}
} // à la deuxième modification, exception de mise à jour
} // si un autre usager a modifié la même JPA, une exception est
// car la version a changé depuis la dernière modification
// levée car la version a changé depuis la dernière modification
Faculté I&C, Claude Petitpierre Faculté I&C, Claude Petitpierre

MySQL
Transactions gérées par version Pour initialiser MySQL sur les stations de la salle de l'EPFL, aller à la
page http://www.mysql.com/downloads/mysql/ et charger l'avant-dernier
(gestion correcte sans concurrence) fichier .zip dans votre répertoire.
Ouvrir une fenêtre terminal, aller dans le répertoire bin du nouveau
public void modifierAvecLecture() { répertoire et appeler
try { mysqld –u root –console ou simplement mysqld
javax.persistence.EntityManager em = ejb3_utility.Manager.open();
Les mots de passe, si vous en utilisez, doivent être entrés dans le
fichier persistence.xml. Ouvrir ensuite une nouvelle fenêtre et appeler:
Query result = em.createQuery(
"SELECT p FROM Produit2 p WHERE p.nom='" + nom + "'"); mysql
Produit2 prod = (Produit2) result.getSingleResult(); Vous pouvez ensuite taper des commandes telles celles-ci:
// prod obtient la dernière valeur de la version
use test
// suite comme dans le transparent précédent
show tables;
select * from vin;

Faculté I&C, Claude Petitpierre 51 Faculté I&C, Claude Petitpierre 52

App Engine de Google


http://code.google.com/intl/fr/appengine/

Download the Google Plugin for Eclipse


Exercice
Créez un compte

Dans Eclipse: New > Web Application Project Æ


• Entrer les produits de l'épicerie dans la base de
données Clic droit sur le nom de projet > Web Application
(exécution comme Tomcat)
• Voir résumé sur JPA:
http://ltiwww.epfl.ch/~petitpie/ProgrammationInternet/JPA_Aux/JPA.html
Clic sur dans le menu principal > remplir champs
(exécution sur serveur Google)
Erreurs: Sign up > application-id > logs

Fichiers pour créer un "disque" dans AppEngine:


http://lti.epfl.ch/Documents09_10/AppEngine/AppEngine.zip
Faculté I&C, Claude Petitpierre 53

Documentation des JPAs

Essais avec JPA:


Remplacer / copier les fichiers ci-dessous dans le projet
Copier persistence.xml Æ src/META-INF
http://ltiwww.epfl.ch/~petitpie/ProgrammationInternet/Google

Documentation:
Site App Engine > Getting started
• Getting started > Java (overview)
• Java (JPA)