Vous êtes sur la page 1sur 51

Java EE 7

Gestion des objets


persistants

2014/2015 Java Entreprise Edition : JEE 7 1

plan
Interrogation dune entit
Le gestionnaire dentits
Obtenir un gestionnaire dentits
Contexte de persistance
Manipulation des entits
LAPI de cache
JPQL
Select, From, Where, Oreder By, Group By et Having, Suppressions
multiples et Mises jour multiples.
Interrogation des entits
Mthodes de rappel et couteurs
Requtes
Requtes dynamiques, Requtes nommes, Requtes natives, Requtes
pour procdures stockes
Concurrence
Gestion de version, Verrouillage optimiste, Verrouillage pessimiste
Cycle de vie dentit
Mthodes de rappel

2014/2015 Java Entreprise Edition : JEE 7 2

1
Gestion des objets persistants
JPA a deux aspects.
Associer des objets une base de donnes relationnelle.
Interrogation de ces objets une fois quils ont t associs une base.
Le gestionnaire dentits, lment central de JPA, permet de crer, rechercher,
supprimer et synchroniser les objets avec la base et permet dexcuter
diffrentes sortes de requtes JPQL sur les entits, comme des requtes
dynamiques, statiques ou natives.
Le gestionnaire dentits autorise galement la mise en place de mcanismes
de verrouillage sur les donnes.
Pour le monde base de donnes relationnelles repose sur SQL qui doit tre
dform pour convenir un langage objets (java). Cest l que JPQL (Java
Persistence Query Language) entre en jeu.
JPQL est le langage quutilise JPA pour interroger les entits stockes dans une
base de donnes relationnelle.
JPQL manipule des objets et des attributs et ne voit pas la structure de la base.

2014/2015 Java Entreprise Edition : JEE 7 3

Interrogation dune entit


Soit une requte simple : trouver un livre par son identifiant.
Soit lentit Book utilisant @Id pour informer le fournisseur de persistance que
lattribut id doit tre associ une cl primaire
@Entity
public class Book {
@Id private Long id;
private String title; private Float price;
private String description; private String isbn;
private Integer nbOfPage; private Boolean illustrations;
// Constructeurs, getters et setters
}
Lentit Book contient les informations par dfaut. Les donnes sont stockes
dans une table portant le mme nom que lentit (BOOK) et chaque attribut est
associ une colonne.

2014/2015 Java Entreprise Edition : JEE 7 4

2
Interrogation dune entit
On utilise une classe Main qui utilise linterface EntityManager pour stocker une instance
de Book dans la table.
public class Main {
public static void main(String[] args) {
// Creates an instance of book
Book book = new Book(); book.setId(1234L);
book.setTitle("The Hitchhiker's Guide to the Galaxy"); book.setPrice(12.5F);
book.setDescription("Science fiction comedy series created by Douglas Adams.");
book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); book.setIllustrations(false);
// Gets an entity manager and a transaction
EntityManagerFactory emf =
Persistence.createEntityManagerFactory(Constants.PERSISTENCE_UNIT_NAME);
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
// Persists the book to the database
tx.begin(); em.persist(book); tx.commit();
// Retreives the book by its identifier
book = em.find(Book.class, 1234L);
System.out.println(book);
em.close(); emf.close();
} 2014/2015
} Java Entreprise Edition : JEE 7 5

Interrogation dune entit


Le code prcdent utilise quatre tapes pour stocker un livre dans la base de
donnes puis le rcuprer.
1. cration dune instance de lentit Book. Les entits sont des POJO grs
par le fournisseur de persistance. Du point de vue de java, une instance est cre
par new. A ce stade le fournisseur ne connait pas encore lobjet Book.
2. cration dun gestionnaire dentits et dune transaction. On cre dabord
une fabrique de gestionnaires dentits pour lunit de persistance chapter04PU.
Cette fabrique sert fournir un gestionnaire (em) qui permet de crer une
transaction (tx), puis stocker et rcuprer un objet Book.
3. stockage du book dans la base de donnes. On lance une transaction
(tx.begin()) et on utilise la mthode EntityManager.persist() pour insrer une
instance de Book. Lorsque la transaction est valide (tx.commit()), les donnes
sont crites dans la base de donnes.
4. rcupration dun book par son identifiant. On utilise le gestionnaire
dentits pour retrouver un book partir de son identifiant par
EntityManager.find().
2014/2015 Java Entreprise Edition : JEE 7 6

3
Interrogation dune entit
On na pas utilis des requtes JPQL ni dappels JDBC. La classe main interagit
avec la base de donnes via linterface EntityManager, qui fournit un ensemble
de mthodes qui permettent de raliser des oprations sur lentit Book.
LentityManager utilise le fournisseur de persistance pour interagir avec la base.
Lorsquon appelle lune des mthodes de lEntityManager, le fournisseur produit
et excute une instruction SQL via JDBC correspondant.

2014/2015 Java Entreprise Edition : JEE 7 7

Interrogation dune entit


Quel pilote JDBC utiliser ? Comment se connecter ? Le nom de la base ?
Ces informations sont prcises dans le fichier META-INF/persistence.xml
Lunit de persistance chapter04PU dfinit une connexion JDBC pour la base
chapter04DB. Elle se connecte avec le compte APP et mot de passe APP. Le marqueur
<class> demande au fournisseur de grer la classe Book.
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="chapter04PU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class> com.apress.javaee7.chapter04.Book class>
<properties> <property name="eclipselink.target-database" value="DERBY"/>
<property name="eclipselink.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
<property name="eclipselink.jdbc.url"
value="jdbc:derby://localhost:1527/chapter04DB;create=true"/>
<property name="eclipselink.jdbc.user" value="APP"/>
<property name="eclipselink.jdbc.password" value="APP"/>
</properties> </persistence-unit> </persistence>
2014/2015 Java Entreprise Edition : JEE 7 8

4
Le gestionnaire dentit
Le gestionnaire dentits est une composante essentielle en JPA.
JPA gre ltat et le cycle de vie des entits et les interroge dans un contexte de
persistance.
Cest galement lui responsable de la cration et de la suppression des
instances dentits persistantes et qui les retrouve partir de leur cl primaire.
Il peut les verrouiller pour les protger des accs concurrents en utilisant un
verrouillage optimiste ou pessimiste.
Lorsquun gestionnaire dentits se rfre une entit, celle-ci est dite gre.
Avant cela, lentit est considre comme un POJO classique (elle est
dtache).
Avec JPA, lorsquune entit est gre, il devient possible deffectuer des
oprations de persistance (synchroniser ltat de lentit avec la base).
Lorsquune entit est dtache (non gre), elle devient un simple POJO et
peut tre utilise par les autres couches sans que son tat soit synchronis

2014/2015 Java Entreprise Edition : JEE 7 9

Le gestionnaire dentit

2014/2015 Java Entreprise Edition : JEE 7 10

5
Le gestionnaire dentit

2014/2015 Java Entreprise Edition : JEE 7 11

2014/2015 Java Entreprise Edition : JEE 7 12

6
Obtenir un gestionnaire dentits
Dans un environnement gr par un conteneur, cest ce dernier qui gre les
transactions (pas besoin dappeler begin(), commit(), etc.).
Dans un environnement gr par lapplication, cest cette dernire qui est
responsable de lobtention explicite de lEntityManager et de la gestion de son
cycle de vie (voir le code prcdent).
Dans un environnement gr par conteneur, lapplication sexcute dans une
servlet ou dans un conteneur dEJB.
En Java EE, pour obtenir un gestionnaire dentits consiste soit utiliser
lannotation @PersistenceContext pour en injecter un, soit utiliser JNDI.
Un composant qui sexcute dans un conteneur (servlet, EJB, service web, )
na pas besoin de crer ou de fermer le gestionnaire dentits puisque son cycle
de vie est gr par le conteneur.
Exemple dune session sans tat dans laquelle on injecte une rfrence lunit
de persistance chapter04PU

2014/2015 Java Entreprise Edition : JEE 7 13

Obtenir un gestionnaire dentits


@Stateless
public class BookBean {
@PersistenceContext(unitName =chapter04PU )
private Entitymanager em;
public void createBook(){ // Creation dune instance de Book
Book book = new Book();
book.setId(1234L); book.setTitle("The Hitchhiker's Guide to the Galaxy");
book.setPrice(12.5F);
book.setDescription("Science fiction comedy series created by Douglas Adams.");
book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); book.setIllustrations(false);
// stockage de linstance dans la base de donnes
em.persist(book);
// Rcupration dune instance par son identifiant
book = em.find(Book.class, 1234L);
System.out.println(book);
}
}
2014/2015 Java Entreprise Edition : JEE 7 14

7
Obtenir un gestionnaire dentits
Le code prcdent est bien plus simple:

On n a pas besoin des objets Persistence ou EntityManagerFactory car le


gestionnaire dentits est inject par le conteneur.

Les beans sans tat grent les transactions, on na pas besoin dappeler
commit() ou rollback().
La plupart des entits utilises ici nimplmentent pas linterface
Serializable car elles nen ont pas besoin pour tre persistantes.
Si on a des appels distants, conteneur EJB externe, les entits doivent
implmentes linterface Serializable pour indiquer au compilateur quil faut
que tous les champs de lentit soient srialisables afin quune instance
puisse tre srialise dans un flux doctets et passe par RMI (Remote
Method Invocation).

2014/2015 Java Entreprise Edition : JEE 7 15

Contexte de persistance
Le contexte de persistance est lensemble des instances dentits gres un
instant donn.
Dans un contexte de persistance, il ne peut exister quune seule instance
dentit avec le mme identifiant de persistance (si un Book didentifiant 1234
existe dans le contexte de persistance, aucun autre Book portant le mme
identifiant ne peut exister dans le mme contexte).
Seules les entits contenues dans le contexte de persistance sont gres par le
gestionnaire dentits (leurs modifications sont refltes dans la base).
Le gestionnaire dentits modifie ou consulte le contexte de persistance
chaque appel dune mthode de linterface EntityManager.
Exemple : - lorsque la mthode persist() est appele, lentit passe en
paramtre est ajoute au contexte de persistance si elle ne sy trouve pas.
- lorsquon recherche une entit avec son identifiant, le gestionnaire dentits
vrifie dabord si elle existe dj dans le contexte de persistance.

2014/2015 Java Entreprise Edition : JEE 7 16

8
Contexte de persistance
Le contexte de persistance est considr comme un cache de premier niveau :
cest un espace rduit o le gestionnaire stocke les entits avant dcrire son
contenu dans la base. Les objets ne vivent dans le contexte de persistance que
le temps de la transaction.
Pour crer le gestionnaire dentits, on doit configurer une unit de persistance.
Ceci est dfinie dans META-INF/persistence.xml qui prcise les informations
ncessaires pour la connexion la base.
Lunit de persistance est le pont qui relie le contexte de persistance et la base.
Les marqueurs <class> donnent la liste des entits pouvant tre gres dans le
contexte de persistance .
Ici, on est dans un environnement gr par lapplication (transaction-type =
RESOURCE_LOCAL) alors que dans un environnement gr par un
conteneur, le fichier persistence.xml dfinirait une source de donnes la place
des infos de connexion et le type de transaction serait JTA (transaction-type =
JTA).

2014/2015 Java Entreprise Edition : JEE 7 17

Manipulation des entits


Le gestionnaire dentits sert aussi crer des requtes JPQL complexes pour
rcuprer une ou plusieurs entits.
Lorsquelle manipule des entits uniques, linterface EntityManager peut tre
considre comme un DAO (Data Access Object) gnrique permettant
deffectuer les oprations CRUD sur nimporte quelle entit.
Mthode Description
void persist(Object entity) Cre une instance gre et persistante
<T> T find(Class<T> entityClass, Recherche une entit de la classe et de la cl indiques
Object primary (key))
<T> T getReference(Class<T> Obtient une instance dont ltat peut tre rcupr de
entityClass, Object primary (key)) faon paresseuse
void remove (Object entity) Supprime linstance dentit du contexte de persistance et
de la base
<T> T merge(T entity) Fusionne ltat de lentit indique dans le contexte de
persistance courant

2014/2015 Java Entreprise Edition : JEE 7 18

9
Manipulation des entits
Mthode Description
void refresh (Object entity) Rafraichit ltat de linstance partir de la base en
crasant les ventuelles modifications apportes lentit.
void flush() Synchronise le contexte de persistance avec la base de
donnes
void clear() Vide le contexte de persistance. Toutes les entits gres
deviennent dtaches
void clear(Object entity) Supprime lentit indique du contexte de persistance
Boolean contains(Object entity) Teste si linstance est une entit gre appartenant au
contexte de persistance courant

Soit une relation 1-1 unidirectionnelle entre Customer et son Address. Ces deux
entits ont des identifiants produits automatiquement, et Customer rcupre
lAddress de faon paresseuse (lorsquil en a besoin).

2014/2015 Java Entreprise Edition : JEE 7 19

Manipulation des entits


@Entity
public class Customer {
@Id @GeneratedValue private Long id;
private String firstName; private String lastName; private String email;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "address_fk")
private Address address;
// Constructeurs, getters et setters
}
@Entity
public class Address {
@Id @GeneratedValue private Long id;
private String street1; private String city;
private String zipcode; private String country;
// Constructeurs, getters et setters
}
2014/2015 Java Entreprise Edition : JEE 7 20

10
Manipulation des entits
Les deux entits prcdentes seront traduites dans la base de donnes avec la
structure prsente ci-dessous.
On note que la colonne ADRESS_FK est la colonne de type cl trangre
permettant daccder ADDRESS.
On suppose que lattribut em est de type EntityManager et que tx est de type
EntityTransaction.

2014/2015 Java Entreprise Edition : JEE 7 21

Rendre une entit persistante


Rendre une entit persistante signifie que lon insre les donnes dans la base
si elles ne sy trouvent pas dj (sinon une exception est lance).
Pour ce faire, on cre une instance de lentit avec new, initialiser ses attributs,
lier une entit une autre lorsquil y a des relations et, appeler la mthode
EntityMnanager.persist().
Customer customer = new Customer("Antony", "Balla", "tballa@mail.com");
Address address = new Address("Ritherdon Rd", "London", "8QE", "UK");
customer.setAddress(address);
tx.begin();
em.persist(customer);
em.persist(address);
tx.commit();

assertNotNull(customer.getId());
assertNotNull(address.getId());

2014/2015 Java Entreprise Edition : JEE 7 22

11
Rendre une entit persistante
partir de linstant dappel de em.persist(), les deux objets deviennent candidats
une insertion dans la base.
Lorsque la transaction est valide (tx.commit()), les donnes sont crites dans la
base.
Lentit Customer tant la propritaire de la relation, sa table contient une cl
trangre vers ADRESS.
Les deux expressions assertNotNull testent que les deux entits ont bien reu un
identifiant (fourni automatiquement par le fournisseur grce aux annotations).
On note lordre dappel des mthodes persist() : on rend le client persistant, puis
son adresse (mme si on avait fait linverse le rsultat est le mme car le
gestionnaire dentits met les donnes en cache et, lorsquil est prt, les crit
dans lordre quattend la base pour respecter les contraintes dintgrit).
cause de la cl trangre que contient la table CUSTOMER, linstruction insert
dans ADRESS doit seffectuer en premier, suivie de celle de CUSTOMER.

2014/2015 Java Entreprise Edition : JEE 7 23

Recherche par identifiant


Pour trouver une entit par son identifiant, on peut utiliser deux mthodes :
1- EntityManager.find() : prend deux paramtres; la classe de lentit et
lidentifiant. Elle renvoie lentit si elle est trouve ou null sinon.
Customer customer=em.find(Customer.class, 1234L);
if (customer !=null){ //traiter lobjet }
2- EntityManager.getReference() : mme paramtres que find(). Elle permet
de rcuprer une rfrence sur lentit partir de sa cl primaire, pas partir de
ses donnes. Lors de lappel, les donnes de ltat sont rcupres de faon
paresseuse, si lon naccde pas ltat avant que lentit ne soit dtache, les
donnes peuvent tre manquantes. Si lentit nest pas trouve une exception
est leve EntityFoundException.
try {
Customer customer=em.getReference(Customer.class, 1234L);
//traiter lobjet
} catch(EntityNotFoundException ex) { //entit non trouve
}
2014/2015 Java Entreprise Edition : JEE 7 24

12
Suppression dune entit
EntityManager.remove() : supprime une entit qui est aussi te de la base,
dtache du gestionnaire dentits et qui ne peut plus tre synchronise avec la
base.
Customer customer = new Customer("Antony", "Balla", "tballa@mail.com");
Address address = new Address("Ritherdon Rd", "London", "8QE", "UK");
customer.setAddress(address);
tx.begin(); em.persist(customer); em.persist(address); tx.commit();

tx.begin(); em.remove(customer); tx.commit();


//les donnes sont supprimes de la base mais lobjet reste accessible
assertNotNull(customer);
Dans la base, la ligne Customer est lie son Address via une cl trangre.
Le code ne supprime que lentit Customer, linstance dAddress nest
rfrence par aucune autre entit. La ligne dadresse est orpheline.

2014/2015 Java Entreprise Edition : JEE 7 25

Suppression des orphelins


Pour la cohrence des donnes, il faut viter de produire des orphelins (des
lignes de la base non rfrences par aucune autre table).
Avec JPA, on peut demander de supprimer automatiquement les orphelins.
Si une entit cible (Address) appartient uniquement une source (Customer) et
que la source soit supprime par lapplication, le fournisseur doit galement
supprimer la cible.
Les relations 1-1 ou 1-N disposent dune option demandant la suppression des
orphelins avec llment orphanRemoval = true de @OneToOne :
@Entity
public class Customer {
@Id @GeneratedValue private Long id;
private String firstName; private String lastName; private String email;
@OneToOne (fetch = FetchType.LAZY, orphanRemoval = true)
private Address address;
// Constructeurs, getters et setters
} // Address est supprime automatiquement lorsque Customer est supprim
2014/2015 Java Entreprise Edition : JEE 7 26

13
Synchronisation avec la base de donnes

La synchronisation avec la base est effectue lorsque la transaction est


valide. Le gestionnaire dentits est un cache qui attend cette validation
pour crire des donnes dans la base.
tx.begin();
em.persist(customer);
em.persist(address);
tx.commit();
On ne sait pas exactement quand le fournisseur crira vraiment les
donnes dans la base.

On peut explicitement crire des donnes dans la base (flush()) ou,


inversement, rafrachir des donnes partir de la base (refresh()).

rollback() annule la transaction et les donnes crites dans la base


seront supprimes.

2014/2015 Java Entreprise Edition : JEE 7 27

Synchronisation avec la base de donnes


EntityManager.flush() force le fournisseur de persistance crire les donnes
dans la base.
tx.begin(); em.persist(customer); em.flush(); em.persist(address); tx.commit();
em.flush() : nattendra pas que la transaction soit valide pour crire le contexte
de persistance dans la base.
Ce code ne fonctionne pas cause des contraintes dintgrit. Sans flush()
(criture explicite), le gestionnaire dentits met en cache toutes les
modifications, les ordonne et les excute de faon cohrente du point de vue de
la base.
Avec flush(), linstruction insert sur la table CUSTOMER sexcute mais la
contrainte dintgrit sur la cl trangre (colonne address_fk de CUSTOMER)
sera viole et la transaction est annule. Les donnes crites seront
supprimes de la base.
On doit faire attention avec lutilisation de flush() (criture explicite), et ne
lutiliser que cest ncessaire.

2014/2015 Java Entreprise Edition : JEE 7 28

14
Synchronisation avec la base de donnes
La mthode refresh() effectue une synchronisation dans la direction oppose
de flush() c.--d. quelle crase ltat courant dune entit gre avec les
donnes qui se trouvent dans la base.
On lutilise lorsquil sagit dannuler des modifications qui ont t effectues sur
lentit en mmoire.
Exemple : on recherche un client par son identifiant, et on modifie son prnom
et on annule ce changement en appelant refresh().
Customer customer = em.find(Customer.class, 1234L);
assertEquals(customer.getFirstName(), "Regragui");
customer.setFirstName("Ahmed");

em.refresh(customer);
assertEquals(customer.getFirstName(), "Regragui");

2014/2015 Java Entreprise Edition : JEE 7 29

Contenu du contexte de persistance


Le contexte de persistance contient les entits gres. Grace linterface
EntityManager, on peut tester si une entit est gre et supprimer toutes les
entits du contexte de persistance.
EntityManager.contains() renvoie un boolean indiquant si une instance dentit
est actuellement gre par le gestionnaire dentits dans le contexte courant.
Customer customer = new Customer("Antony", "Balla", "tballa@mail.com");
tx.begin(); em.persist(customer); tx.commit();
assertTrue(em.contains(customer));

tx.begin(); em.remove(customer); tx.commit();

assertFalse(em.contains(customer));
On a un customer persistant. On peut vrifier que lentit est gre
(em.contains(customer) renvoie true). Puis on supprime cette entit de la base
et lappel de em.contains(customer) renvoie alors false.

2014/2015 Java Entreprise Edition : JEE 7 30

15
Contenu du contexte de persistance
La mthode em.clear() vide le contexte de persistance : toutes les entits qui
taient gres deviennent dtaches.
em.clear(object entity) agit uniquement sur une entit.
em.detach(Object entity) supprime lentit indique du contexte de
persistance. Les modifications apportes cette entit ne sont plus
synchronises avec la base de donnes.
Customer customer = new Customer("Antony", "Balla", "tballa@mail.com");
tx.begin(); em.persist(customer); tx.commit();
assertTrue(em.contains(customer));

em.detach(customer);
assertFalse(em.contains(customer));

assertFalse(em.contains(customer)) vrifie que lentit customer est bien


dtache.

2014/2015 Java Entreprise Edition : JEE 7 31

Contenu du contexte de persistance


Une entit dtache nest plus associe un contexte de persistance. Si on
veut la grer, on doit la fusionner avec em.merge(Object entity).
Customer customer = new Customer("Antony", "Balla", "tballa@mail.com");
tx.begin(); em.persist(customer); tx.commit();
em.clear();
//modifie une valeur dune entit dtache.
customer.setFirstName(("William");

tx.begin(); em.merge(customer); tx.commit();


on vide le contexte de persistance avec em.clear() afin de dtacher lentit
customer mais les entits dtaches continuent de vivre en dehors du contexte
de persistance dans lequel elles taient. La synchronisation de leur tat avec
celui de la base nest plus garantie.
Ici customer.setFirstName("William") est excute sur une entit dtache et
les donnes ne sont pas modifies dans la base. Pour effectuer cette
modification, il faut rattacher lentit (la fusionner avec em.merge(customer)).
2014/2015 Java Entreprise Edition : JEE 7 32

16
Contenu du contexte de persistance
Lorsquune entit est gre (non dtache), les modifications quon lui apporte
sont automatiquement refltes.
Si lentit nest pas gre, on doit appeler em.merge().
Customer customer = new Customer("Antony", "Balla", "tballa@mail.com");
tx.begin();
em.persist(customer);
customer.setFirstName("William");
tx.commit();
Lappel de la mthode customer.setFirstName() modifie ltat de lentit. Le
gestionnaire dentits met en cache toutes les actions excutes partir de
tx.begin() et ne les rpercute dans la base que lorsque la transaction est valide
avec tx.commit().

2014/2015 Java Entreprise Edition : JEE 7 33

Contenu du contexte de persistance


Par dfaut, chaque opration du gestionnaire dentits ne sapplique qu lentit
passer en paramtre lopration.
Parfois, on souhaite propager laction de lentit ses relations : cest ce quon
appelle rpercuter un vnement (rpercussion dvnements).
Les exemples donnes jusquici reposent sur ce comportement par dfaut
Customer customer = new Customer("Antony", "Balla", "tballa@mail.com");
Address address = new Address("Ritherdon Rd", "London", "8QE", "UK");
customer.setAddress(address);

tx.begin(); em.persist(customer); em.persist(address); tx.commit();

Comme il existe une relation entre Customer et Address, on peut rpercuter


laction persist() du Customer vers son Address. Ceci signifie quun appel
em.persist(customer) rpercutera lvnement persist lentit Address si elle
autorise la propagation de ce type dvnement.
Le code prcdent peut tre allg en supprimant em.persist(address).

2014/2015 Java Entreprise Edition : JEE 7 34

17
Contenu du contexte de persistance
Pour quune rpercussion ait lieu, lassociation de la relation doit tre modifie.
Les annotations @OneToOne, @OneToMany, @ManyToOne et
@ManyToMany disposent dun attribut cascade pouvant recevoir un tableau
dvnements propager.
On doit donc modifier lassociation de lentit Customer en ajoutant un attribut
cascade lannotation @OneToOne. Ici, on va propager persist et remove, afin
que la suppression dun client entrane celle de son adresse
@Entity
public class Customer {
@Id @GeneratedValue private Long id;
private String firstName; private String lastName; private String email;
@OneToOne (fetch = FetchType.LAZY, cascade= { CascadeType.PERSIST,
CascadeType.REMOVE })
private Address address;
// Constructeurs, getters et setters
}
2014/2015 Java Entreprise Edition : JEE 7 35

Contenu du contexte de persistance


Dans le tableau ci-dessous, on numre les vnements quon peut
propager vers une cible de relation.
On peut mme tous les propager en utilisant le type CascadeType.ALL.

Type Description
PERSIST Propage les oprations persist la cible de la relation
REMOVE Propage les oprations remove la cible de la relation
MERGE Propage les oprations merge la cible de la relation
REFRESH Propage les oprations refresh la cible de la relation
CLEAR Propage les oprations clear la cible de la relation
ALL Propage toutes les oprations prcdentes

2014/2015 Java Entreprise Edition : JEE 7 36

18
LAPI de cache
On a dj mentionn que le gestionnaire dentits est un
cache de premier niveau utilis pour traiter les donnes afin
quelle conviennent la base et mettre en cache les entits
en cours dutilisation.

Ce cache permet de rduire le nombre de requtes SQL de


chaque transaction. Si un objet est modifi plusieurs fois au
cours de la mme transaction, le gestionnaire dentits ne
produira quune seule instruction update la fin de cette
transaction.

2014/2015 Java Entreprise Edition : JEE 7 37

LAPI de cache

2014/2015 Java Entreprise Edition : JEE 7 38

19
LAPI de cache
Dans la figure prcdente, deux utilisateurs ont besoin d'accder des
entits dont les donnes sont stockes dans la base de donnes.
Chaque utilisateur a son propre contexte de persistance qui dure
pendant toute la dure de sa propre transaction.
Lutilisateur 1 obtient les entits identifies par 12 et 56 partir de la
base de donnes, afin que les deux soient stockes dans son contexte
de persistance.
Lutilisateur obtient les entits identifies par 12 et 34.
L'entit identifie par 12 est stocke dans le contexte de persistance de
chaque utilisateur. Bien que la transaction s'excute, le contexte de
persistance agit comme un cache de premier niveau pour stocker les
entits qui peuvent tre gres par lEntityManager. Une fois la
transaction est termine, le contexte de persistance se termine et les
entits sont effacs.
2014/2015 Java Entreprise Edition : JEE 7 39

Cache du second niveau

Toutes les implmentations


de JPA utilisent un cache de
performance (cache de
second niveau) pour
optimiser les accs la base
de donnes, les requtes,
les jointures, etc.
Les caches de second
niveau rduisent le trafic
avec la base car ils
conservent les objets en
mmoire et les rendent
disponibles toutes
lapplication.

2014/2015 Java Entreprise Edition : JEE 7 40

20
JPQL : Java Persistence Query Language
On vient de voir comment manipuler sparment les entits avec
lEntityManager.
On sait rcuprer une entit partir de son identifiant, la supprimer, modifier
ses attributs, etc.
Mais rechercher une entit par son identifiant est assez limit, en pratique, on a
besoin de rcuprer une ou plusieurs entit(s) satisfaisant certaines conditions
(les clients qui habitent Rabat, ). Cette possibilit est propre aux bases de
donnes et JPA dispose du langage JPQL permettant ce type dinteractions.
JPQL sert dfinir des recherches dentits persistantes indpendamment de la
base de donnes correspondante.
Cest un langage de requte inspir de SQL (Structured Query Language).
La diffrence principale est que le rsultat de SQL est un ensemble de lignes
alors que celui de JPQL est une entit ou une collection dentits.

2014/2015 Java Entreprise Edition : JEE 7 41

JPQL : Java Persistence Query Language


La syntaxe de JPQL est oriente objet et utilise un mcanisme de traduction
pour transformer une requte JPQL en langage comprhensible par une base
SQL.
La requte sexcute sur la base de donnes correspondante avec SQL et des
appels JDBC, puis les instances dentits sont initialises et sont envoyes
lapplication.
La requte la plus simple slectionnant toutes les instances dune seule entit :
SELECT b
FROM Book b
JPQL slectionne des entits, Book. La clause FROM permet galement de
donner un alias cette entit (ici b pour Book). La clause SELECT indique le
type de la requte est lentit b (Book). Le rsultat est une liste de zros ou
plusieurs instances de Book.

2014/2015 Java Entreprise Edition : JEE 7 42

21
JPQL : select
SELECT <expression select>
FROM <clause from>
[WHERE <expression conditionnelle>]
[ORDER BY <clause order by>]
[GROUP BY <clause group by>]
[HAVING <clause having>]
La clause select porte sur une expression qui peut tre une entit, un attribut
dentit, une expression constructeur, une fonction agrgat ou toute squence
de ce qui prcde.
SELECT c.firstName, c.lastName
FROM Customer c // affiche une liste de prnom et de nom du client
Si lentit Customer est en relation 1-1 avec Address, c.address dsigne
ladresse du client .
SELECT c.adress
FROM Customer c // affiche une liste dadresses

2014/2015 Java Entreprise Edition : JEE 7 43

JPQL : select
SELECT NEW com.apress.CustomerDTO( c.firstName, c.lastName , c.address.street1)
FROM Customer c
Le rsultat est une liste dobjets CustomerDTO instancis avec loprateur new
et initialiss avec le prnom, le nom et la rue des clients.
Pour supprimer des doublons, on utilise loprateur DISTINCT :
SELECT DISTINCT c.firstName
FROM Customer c
La clause SELECT peut utiliser les fonctions agrgat AVG, COUNT, MAX, MIN
et SUM. De plus, leurs rsultats peuvent tre regroups par une clause GROUP
BY et filtrs par une clause HAVING.
SELECT COUNT( c)
FROM Customer c
Les clauses SELECT, WHERE et HAVING peuvent utiliser expressions
scalaires (ABS, SQRT, MOD, ), des chaines (CONCAT, SUBSTRING,
LENGTH, ) et des dates (CURRENT_DATE, CURRENT_TIME,).

2014/2015 Java Entreprise Edition : JEE 7 44

22
JPQL : from, where
La clause FROM dfinit les entits en dclarant des variables didentification ou
alias qui sont utiliss dans les autres clauses(select, where, ...).
La clause WHERE est forme dune expression conditionnelle permettant de
restreindre le rsultat dune instruction select, update ou delete.
SELECT c
FROM Customer c
WHERE c.firstName=REDA AND c.address.country=Maroc
La clause WHERE utilise les oprateurs de comparaison : =, >, <, <=, >=, <>,
[NOT] BETWEEN, [NOT] LIKE, [NOT] IN, IS [NOT] NULL, IS [NOT] EMPTY et
[NOT] MEMBER [OF]
SELECT c
FROM Customer c
WHERE (c.age NOT BETWEEN 40 AND 50) OR (c.email LIKE %mail.com)

2014/2015 Java Entreprise Edition : JEE 7 45

JPQL : where
La clause WHERE nutilise pas que des valeurs fixes, mais aussi des
paramtres : par valeur ou par nom.
Les paramtres positionnels sont indiqus par un point dinterrogation suivi dun
entier (?1, par exemple).
Lorsque la requte sera utilise, il faut fournir les valeurs qui viendront
remplacer ces paramtres.
SELECT c
FROM Customer c
WHERE c.firstName=?1 AND c.address.country=?2
Les paramtres nomms sont reprsents par un identifiant de type String
prfix par le caractre deux-points (:). Lorsque la requte sera utilise, il faudra
fournir des valeurs ces paramtres nomms.
SELECT c
FROM Customer c
WHERE c.firstName= :fname AND c.address.country= :country

2014/2015 Java Entreprise Edition : JEE 7 46

23
JPQL : sous requte
Une sous-requte est une requte SELECT intgre dans lexpression
conditionnelle dune clause WHERE ou HAVING.

Le rsultat de cette sous-requte est valu et interprt dans lexpression


conditionnelle de la requte principale.

Pour, obtenir les clients les plus jeunes de la base de donnes, on excute
dabord une sous-requte avec MIN(age) et lon value son rsultat dans la
requte principale :
SELECT c
FROM Customer c
WHERE c.age = (SELECT MIN(c.age) FROM CUSTOMER c)

2014/2015 Java Entreprise Edition : JEE 7 47

JPQL : Order By
La clause ORDER BY permet de trier les entits ou les valeurs renvoyes par
une requte SELECT.
Le tri sapplique lattribut prcis dans cette clause. Si cet attribut est suivi
dASC ou daucun mot-cl, le tri sera ascendant; sil est suivi de DESC, le tri
sera descendant.
SELECT c
FROM Customer c
WHERE c.age >18
ORDER BY c.age DESC

SELECT c
FROM Customer c
WHERE c.age >18
ORDER BY c.age DESC, c.address.country ASC

2014/2015 Java Entreprise Edition : JEE 7 48

24
JPQL : Group By et Having
La clause GROUP BY permet de regrouper des valeurs du rsultat en fonction
dun ensemble de proprits.
Les entits sont alors divises en groupes selon les valeurs de lexpression de la
clause GROUP BY.
Pour regrouper les clients par pays et les compter, on utilise la requte suivante :
SELECT c.address.country, count(c)
FROM Customer c
GROUP BY c.address.country
// HAVING count(c)>100 // les pays ayant plus de 100 clients
GROUP BY dfinit les expressions de regroupement (c.address.country) qui
serviront agrger et compter les rsultats.
On note que les expressions qui apparaissent dans GROUP BY doivent
apparaitre dans SELECT.
HAVING dfinit un filtre qui sapplique aprs le regroupement des rsultats, un
peu comme un second WHERE qui filtre le rsultat de GROUP BY(//exemple).
2014/2015 Java Entreprise Edition : JEE 7 49

JPQL : suppression et mises jour


multiples
Pour supprimer une entit : em.remove()
Pour supprimer un grand nombre dentits en une seule opration, on utilise
DELETE qui ressemble SELECT car elle utilise WHERE et prends des
paramtres. Elle renvoie le nombre dentits concernes par lopration.
DELETE FROM <nom entit [[AS] <variable identification>]
[WHERE <expression conditionnelle>]

Exemple : DELETE FROM Customer c


supprimer tous les clients gs de moins de 18 ans WHERE c.age < 18
L instruction UPDATE permet de modifier toutes les entits rpondant aux
critres de sa clause WHERE.
UPDATE <nom entit [[AS] <variable identification>]
SET <mise jour> {, <mise jour> } UPDATE Customer c
[WHERE <expression conditionnelle>] SET c.firstName =TROP JEUNE
Pour modifier le prnom de tous nos jeunes clients : WHERE c.age < 18

2014/2015 Java Entreprise Edition : JEE 7 50

25
JPQL : requtes
Comment intgrer dans une application les instructions JPQL ?
JPA 2.1 permet dintgrer cinq sortes de requtes :
Les requtes dynamiques. Il sagit de chanes de requtes JPQL indiques
dynamiquement au moment de lexcution.
Les requtes nommes. Des requtes statiques et non modifiables.
Les requtes natives. Elles permettent dexcuter une instruction SQL native
la place dune instruction JPQL.
API des critres : concept introduit par JPA 2.0.
Les requtes de procdures stockes: Nouveau avec JPA 2.1 pour appeler
les procdures stockes.
Le choix entre ces cinq types est centralis au niveau de linterface
EntityManager, qui dispose de plusieurs fabriques renvoyant toutes une
interface Query.

2014/2015 Java Entreprise Edition : JEE 7 51

JPQL : requtes
Mthode Description
Query createQuery(String jpqlString) Cre une instance de Query permettant
dexcuter une instruction JPQL pour des
requtes dynamiques
Query createQuery(QueryDefinition qdef) Cre une instance de Query permettant
dexcuter une requte par critre
Query createNamedQuery(String name) Cre une instance de Query permettant
dexcuter une requte nomme (en JPQL ou
en SQL natif)
Query createNativeQuery(String sqlString) Cre une instance de Query permettant
dexcuter une instruction SQL native
Query createNativeQuery(String sqlString, Cre une instance de Query permettant
Class resultClass) dexcuter une instruction SQL native en lui
passant la classe du rsultat attendue

2014/2015 Java Entreprise Edition : JEE 7 52

26
JPQL : requtes
Mthode Description
Query createNativeQuery(String Cre une instance de Query permettant
jpqlString, String resultSetMapping) dexcuter une instruction SQL en lui passant
un set Map du rsultat attendu.
<T> TypedQuery<T> createQuery Cre une instance de TypedQuery permettant
(CriteriaQueryDefinition <T> criteriaQuery) dexcuter une requte par critre
<T> TypedQuery<T> createQuery (String Cre une instance de TypedQuery permettant
jpqlString, Class<T> resultClass ) dexcuter une requte du rsultat attendu

<T> TypedQuery<T> createQuery (String Cre une instance de TypedQuery permettant


name, Class<T> resultClass ) dexcuter une requte du rsultat attendu

2014/2015 Java Entreprise Edition : JEE 7 53

JPQL : requtes
Mthode Description
StoredProcedureQuery Cre une StoredProcedureQuery pour
createStoredProcedureQuery (String excuter la procdure stocke dans
procedureName) la base.
StoredProcedureQuery Une requte de procdure stocke avec
createStoredProcedureQuery (String passage des classes pour lesquelles les
procedureName, Class... resultClasses) rsultats sont mapps
StoredProcedureQuery Une requte de procdure stocke avec
createStoredProcedureQuery (String passage des rsultats mapps
procedureName, String... resultSetMappings)
StoredProcedureQuery Cre une requte pour une procdure
createNamedStoredProcedureQuery (String stocke nomme
name)

2014/2015 Java Entreprise Edition : JEE 7 54

27
JPQL : requtes
LAPI Query, est utilisable avec les requtes statiques (requtes nommes) et
les requtes dynamiques en JPQL, ainsi quavec les requtes natives en SQL.
Cette API permet galement de lier des paramtres aux requtes et de
contrler la pagination.

2014/2015 Java Entreprise Edition : JEE 7 55

JPQL : requtes

2014/2015 Java Entreprise Edition : JEE 7 56

28
JPQL : requtes

Les mthodes les plus utilises de cette API sont celles qui excutent la
requte.
Pour effectuer une requte SELECT, on doit choisir entre deux mthodes en
fonction du rsultat quon doit obtenir :
La mthode getResultList() excute la requte et renvoie une liste de rsultats
(entits, attributs, expressions, etc.).
La mthode getSingleResult() excute la requte et renvoie un rsultat unique.
Pour excuter une mise jour ou une suppression, on utilise la mthode
executeUpdate(), qui excute la requte et renvoie le nombre dentits
concernes par son excution.
Une requte peut prendre des paramtres nomms (:monParam) ou
positionnels (?2).
LAPI Query dfinit plusieurs mthodes setParameter() pour initialiser ces
paramtres avant lexcution dune requte.

2014/2015 Java Entreprise Edition : JEE 7 57

JPQL : requtes
Une requte peut renvoyer un grand nombre de rsultats. Pour contrler la
pagination, les mthodes setFirstResult() et setMaxResults() permettent
respectivement dindiquer le premier rsultat (en partant de 0) et le nombre
maximal de rsultat.
Le mode flush indique au fournisseur de persistance comment grer les
modifications et les requtes en attente.
Deux modes sont possible : AUTO (par dfaut) et COMMIT. Le premier prcise
que cest au fournisseur de sassurer que les modifications en attente soient
visibles par le traitement de la requte.
COMMIT est utilis lorsque lon souhaite que leffet des modifications apportes
aux entits ncrase pas les donnes modifies dans le contexte de
persistance.
Les requtes peuvent tre verrouilles par setLockMode(LockModeType)

2014/2015 Java Entreprise Edition : JEE 7 58

29
JPQL : requtes dynamiques
Les requtes dynamiques sont dfinies la vole par lapplication lorsquelle en
a besoin.
Elles sont cres par un appel la mthode em.CreateQuery(), qui prend en
paramtre une chane reprsentant une requte JPQL.
Query query =em.createQuery(SELECT c FROM Customer c );
List <Customer> customers = query.getResulList();

La mthode query.getResultList () retourne une liste dobjets non typs. Si on


veut que la mme requte renvoie une liste de type Customer, on doit utiliser la
TypedQuery comme suit:

TypedQuery <Customer> query = em.createQuery ("SELECT c FROM Customer c",


Customer.class);
List<Customer> customers = query.getResultList();

2014/2015 Java Entreprise Edition : JEE 7 59

JPQL : requtes dynamiques


getResultList() renvoie une liste dentits Customer. Si une seule
entit on utilise getSingleResult().

La chane contenant la requte peut galement tre labore


dynamiquement par lapplication (en cours dexcution par loprateur
de concatnation et en fonction de certaines critres) .

String jpqlQuery =SELECT c FROM Customer c ;


If(someCriteria) jpqlQuery += WHERE c.firstName = Ilias ;
query =em.createQuery(jpqlQuery);
List <Customer> customers = query.getResulList();

2014/2015 Java Entreprise Edition : JEE 7 60

30
JPQL : requtes dynamiques
On peut passer le prnom Ilias en paramtre par nom ou par position.
jpqlQuery =SELECT c FROM Customer c ;
If(someCriteria) jpqlQuery += WHERE c.firstName = :fname ;
query =em.createQuery(jpqlQuery);
Query.setParameter(fname, Ilias );
List <Customer> customers = query.getResulList();

On note que le paramtre fname ne contient pas le symbole deux-points.


jpqlQuery =SELECT c FROM Customer c ;
If(someCriteria) jpqlQuery += WHERE c.firstName = ?1 ;
query =em.createQuery(jpqlQuery);
Query.setParameter(1, Ilias);
List <Customer> customers = query.getResulList();

2014/2015 Java Entreprise Edition : JEE 7 61

JPQL : requtes nommes


Si on veut paginer la liste des clients par groupe de 10 :
Query query =em.createQuery(SELECT c FROM Customer c );
query.setMaxResults(10);
List <Customer> customers = query.getResulList();
Le problme des requtes dynamiques est le cot de la traduction de la chane
JPQL en instruction SQL au moment de lexcution.
La requte est cre lexcution, elle ne peut pas tre prvue la compilation.
Il y a un surcot de traitement des requtes dynamiques, il est conseill
dutiliser plutt des requtes statiques (requtes nommes).
Les requtes nommes sont statiques et non modifiables. Il n y a pas la
souplesse des requtes dynamiques.
Lexcution des requtes nommes peut tre efficace car le fournisseur de
persistance peut traduire la chane JPQL en SQL au dmarrage de lapplication
au lieu de le faire chaque fois que la requte est excute.

2014/2015 Java Entreprise Edition : JEE 7 62

31
JPQL : requtes nommes
Les requtes nommes sont exprimes via une annotation @NamedQuery ou
son quivalent XML.
Cette annotation prend deux lments : le nom de la requte et son contenu.
On modifie lentit Customer pour dfinir trois requtes statiques :
@Entity
@NamedQueries({
@NamedQuery(name = "findAll", query = "select c from Customer c"),
@NamedQuery(name = "findVincent", query = "select c from Customer c where
c.firstName = 'Vincent'"),
@NamedQuery(name = "findWithParam", query = "select c from Customer c where
c.firstName = :fname") })
public class Customer { @Id @GeneratedValue private Long id;
private String firstName; private String lastName; private Integer age; private String email;
@OneToOne @JoinColumn(name = "address_fk") private Address address;
// Constructeurs, getters et setters
}
2014/2015 Java Entreprise Edition : JEE 7 63

JPQL : requtes nommes


Lentit Cutomer prcdente dfinit plusieurs requtes nommes.
Lannotation @NamedQueries prend un tableau de @NamedQuery:
La premire requte nomme findAll, renvoie toutes les entits de la base.
La requte findWithParam prend un paramtre fname pour choisir les clients en
fonction de leur prnom.
Si lentit Customer navait quune seule requte, on aurait utilis seulement
@NamedQuery :
@NamedQuery(name = "findAll", query = "select c from Customer c"),
public class Customer { }
Si on a besoin dune requte qui va retourner une liste de Customer, on doit
utiliser TypedQuery comme suit:
TypedQuery<Customer> query = em.createNamedQuery("findAll", Customer.class);
Lexcution de ces requtes ressemble celle des requtes dynamiques : on
appelle em.createNamedQuery() en lui passant le nom de la requte

2014/2015 Java Entreprise Edition : JEE 7 64

32
JPQL : requtes nommes
Pour excuter la requte findAll :
Query query =em.createNamedQuery(findAll );
List <Customer> customers = query.getResulList();
Pour appeler la requte findWithParam en lui passant le paramtre fname et
en limitant le rsultat 3 :
Query query = em.createNamedQuery(findWithParam );
query.setParameter(fname, Ilias);
query.setMaxResults(3);
List <Customer> customers = query.getResulList();
On peut utiliser un raccourci qui permet dappeler
setParameter().setMaxResults(), etc.
Query query = em.createNamedQuery(findWithParam).setParameter(fname,
Ilias).setMaxResults(3);

2014/2015 Java Entreprise Edition : JEE 7 65

JPQL : requtes nommes


Les requtes nommes permettent dorganiser les dfinitions de requtes et
amliorent les performances de lapplication.
La porte du nom de la requte est celle de lunit de persistance et ce nom doit
tre unique dans cette porte (c.--d. il ne peut exister quune seule requte
findAll).
En pratique, on prfixe le nom de la requte par celui de lentit
(Customer.findAll).
On peut remplacer le nom de la requte par une constante.
@Entity
@NamedQuery(name = Customer.FIND_ALL, query = "select c from Customer c")
public class Customer {
public static final String FIND_ALL = "Customer.findAll";
// Attributs, constructeurs, getters et setters
}
Query query = em.createNamedQuery(Customer.FIND_ALL);
List <Customer> customers = query.getResulList();
2014/2015 Java Entreprise Edition : JEE 7 66

33
JPQL : requtes natives
JPA autorise lutilisation des fonctionnalits spcifiques dun SGBDR via des
requtes natives. Celles-ci prennent en paramtre une instruction SQL et
renvoient une instance de Query.
Les requtes natives ne sont pas ncessairement portables dune base
lautre.
On prfre les requtes natives aux appels JDBC, qui sont portables, car le
rsultat de cette requte est automatiquement converti en entits.
Query query = em.createNativeQuery("Select * FROM t_customer ",
Customer.class);
List <Customer> customers = query.getResulList();
Les requtes natives peuvent utiliser le mcanisme des annotations pour dfinir
des requtes SQL statiques.
@Entity
@NamedNativeQuery(name = "findAll", query = "select * from t_customer")
@Table(name="t_customer")
public class Customer { // Attributs, constructeurs, getters et setters }
2014/2015 Java Entreprise Edition : JEE 7 67

LAPI des critres (ou Object-Oriented


Queries)

La requte : SELECT c FROM Customer c WHERE c.firstName = 'Ilias'

Peut scrire en requtes orientes objets :


CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery <Customer> criteriaQuery =builder.createQuery(Customer.class);
Root <Customer> c = criteriaQuery.from (Customer.class);
criteriaQuery.select(c).where (builder.equal(c.get("firstName"), "IIlias"));
Query query = em.createQuery(criteriaQuery).getResultList();
List<Customer> customers = query.getResultList();

Linterface CriteriaBuilder obtenue partir EntityManager contient les


methodes : select(), from(), where(),orderBy(), groupBy(),
Aussi : desc(), asc(), avg(), sum(), max(), min(), count(), and(), or(),
Note : lalias c (en SELECT c FROM Customer) est obtenu par (Root
<Customer> c).
2014/2015 Java Entreprise Edition : JEE 7 68

34
Requtes de procdures stockes

Dans le cas dun traitement complexe qui exige l'excution de


plusieurs instructions SQL ou un usage intensif de donnes pour des
tches rptitives, on utilise des procdures stockes.
Exemple : Aprs un certain temps des livres et des CD sont archivs
dans un certain entrept.
L'archivage de Livres et de CD est un processus long qui permet de
mettre jour plusieurs tables (Inventaire, Warehouse, livre, CD, etc.)
On peut alors crire une procdure stocke pour regrouper plusieurs
tats SQL et amliorer les performances.
Soit sp_archive_books une procdure stocke dfinie comme suit
prend en paramtre archive date et un warehouse code et met jour
les tables T_Inventory et T_Transport.

2014/2015 Java Entreprise Edition : JEE 7 69

Requtes de procdures stockes

CREATE PROCEDURE sp_archive_books @archiveDate DATE,


@warehouseCode VARCHAR AS
UPDATE T_Inventory
SET Number_Of_Books_Left - 1
WHERE Archive_Date < @archiveDate AND Warehouse_Code =
@warehouseCode;

UPDATE T_Transport
SET Warehouse_To_Take_Books_From = @warehouseCode;
END

2014/2015 Java Entreprise Edition : JEE 7 70

35
Requtes de procdures stockes

2014/2015 Java Entreprise Edition : JEE 7 71

Requtes de procdures stockes

Pour invoquer la procdure stocke sp_archive_books, on doit utiliser


le gestionnaire d'entits en lui passant (archiveOldBooks):

On peut enregistrer et appeler une requte : StoredProcedureQuery

2014/2015 Java Entreprise Edition : JEE 7 72

36
JPQL : concurrence
JPA peut modifier des donnes persistantes et JPQL permet de rcuprer des
donnes rpondant certains critres.
Lapplication peut avoir plusieurs threads et une seule base de donnes : il est
donc assez frquent daccder aux entits de faon concurrente.
Lapplication doit contrler la synchronisation des donnes au moyen dun
mcanisme de verrouillage.
JPA 2.0 dispose de deux types de verrouillages (JPA 1.0 disposait seulement du
verrouillage optimiste) :
Le verrouillage optimiste. il repose sur la supposition que la plupart des
transactions nentrent pas en conflit les unes avec les autres, ce qui permet une
concurrence aussi permissive que possible.
Le verrouillage pessimiste. Il fait la supposition inverse, ce qui impose dobtenir
un verrou sur la ressource avant de la manipuler.
Exemple de traverse dune avenue. Dans une zone faible trafic, on peut
traverser sans regarder (traverse optimiste) alors que, dans une zone fort
trafic, il ne faut pas le faire (traverse pessimiste).
2014/2015 Java Entreprise Edition : JEE 7 73

JPQL : concurrence
Le verrouillage peut seffectuer au niveau du gestionnaire dentits et au niveau
Query avec les mthodes numres ci-dessous :
Mthode Description
<T> T find (Class <T> entityClass, Object Recherche une entit de la classe avec la cl
primaryKey, LockModetype lockMode ) indique et la verrouille selon le type du verrou
void lock(Object entity, LockModeType Verrouille une instance dentit contenue dans
lockMode) le contexte de persistance avec le type de
verrou indiqu
void refresh (Object entity, Rafrachit ltat de linstance partir de la base
LockModeType lockMode) en crasant les ventuelles modifications
apportes lentit et verrouille celle-ci selon le
type de verrou indiqu

Mthode Description
Query setLockMode(LockModetype Fixe le type de verrou utilis pour lexcution
lockMode ) de la requte

2014/2015 Java Entreprise Edition : JEE 7 74

37
JPQL : concurrence
Toutes les mthodes prcdentes attendent un paramtre LockModeType qui
peut prendre les valeurs suivantes :
OPTIMISTIC. Verrouillage optimiste.
OPTIMISTIC_FORCE_INCREMENT. verrouillage optimiste et incrmentation
de la colonne version de lentit ( venir).
PESSIMISTIC_READ. Verrouillage pessimiste sans avoir besoin de relire les
donnes la fin de la transaction pour obtenir un verrou.
PESSIMISTIC_WRITE. verrouillage pessimiste et srialisation entre les
transactions pour mettre jour lentit.
PESSIMISTIC_FORCE_INCREMENT. Verrouillage pessimiste et
incrmentation de la colonne version de lentit ( venir).
NONE. Aucun mcanisme de verrouillage nest utilis

2014/2015 Java Entreprise Edition : JEE 7 75

JPQL : concurrence
On peut lire puis verrouiller :
Book book =em.find(Book.class, 12);
// verrouille pour augmenter le prix
em.lock(book, LockModeType.PESSIMISTIC);
book.raisePriceByTwoDollars();

On peut lire et verrouiller


Book book =em.find(Book.class, 12, LockModeType.PESSIMISTIC);
// le livre est dj verrouill : on augmente son prix
book.raisePriceByTwoDollars();

La concurrence et le verrouillage sont les motivations de la gestion des


versions.

2014/2015 Java Entreprise Edition : JEE 7 76

38
JPQL : gestion de version
Java utilise le systme des versions : 1.4, 1.5, 1.6, 1.7
JPA utilise le mme mcanisme lorsquon a besoin de versions dentits.
La premire fois quon rend une entit persistante, elle prend le numro de
version 1.
Si plus tard, on modifie un attribut et quon rpercute cette modification dans la
base de donnes, le numro de version de lentit passe 2, etc.
La version de lentit volue chaque fois quelle est modifie.
Pour ceci, lentit possde un attribut annot par @Version, lui permettant de
stocker son numro de version. Cet attribut est ensuite traduit par une colonne
dans la base.
Les types autoriss pour les numros de version sont int, Integer, short, Short,
long, Long ou Timestamp.

2014/2015 Java Entreprise Edition : JEE 7 77

JPQL : gestion de version


@Entity
public class Book {
@Id @GeneratedValue private Long id;
@Version private Integer version;
private String title; private Float price; private String description;
private String isbn; private Integer nbOfPage; private Boolean illustrations;
// Constructeurs , getters et setters
}
Lentit peut lire la valeur de sa version mais ne peut pas la modifier : seul le
fournisseur de persistance peut initialiser ou modifier cette valeur lorsque lobjet
est crit ou modifi dans la base.
Book book = new Customer("H2G2",21f, "Best It book", "123-456", 321, false);
tx.begin(); em.persist(book); tx.commit();
assertEquals(1, book.getVersion());

tx.begin(); em.raisePriceByTwoDollars(); tx.commit();


assertEquals(2, book.getVersion());
2014/2015 Java Entreprise Edition : JEE 7 78

39
JPQL : gestion de version
Dans le code prcdent, on rend une nouvelle entit Book persistante.

Lorsque la transaction se termine, le fournisseur de persistance fixe son numro


de version 1. Puis on modifie le prix du livre et, aprs lcriture des donnes
dans la base, le numro de version est incrment et vaut donc 2.

Lattribut de version nest pas obligatoire, mais il est conseill lorsque lentit est
susceptible dtre modifie en mme temps par plusieurs processus ou
plusieurs threads.

La gestion de version est au cur du verrouillage optimiste car elle offre une
protection pour les modifications concurrentes des entits.

Lentit est automatiquement gre par verrouillage optimiste lorsquelle utilise


lannotation @Version.

2014/2015 Java Entreprise Edition : JEE 7 79

JPQL : verrouillage optimiste


Le verrouillage optimiste est utilis lorsquon estime quil y a de fortes
chances pour que la transaction qui modifie une entit soit la seule
modifier cette entit cet instant.

La dcision de verrouiller lentit est prise la fin de la transaction, afin de


garantir que les modifications apportes lentit soient cohrentes avec
ltat courant de la base de donnes.

Les transactions qui violent cette contrainte provoquent la leve dune


exception OptimisticLockException et sont annules.

Lutilisation de lannotation @Version permet au gestionnaire dentits


deffectuer un verrouillage optimiste en comparant la valeur de version dans
lentit avec celle dans la base.

2014/2015 Java Entreprise Edition : JEE 7 80

40
JPQL : verrouillage optimiste

Les transactions tx1 et tx2 obtiennent toutes les deux une instance de la mme
entit Book. ce moment la version de lentit est 1.
tx1 augmente le prix de 2$ et valide cette modification : lorsque les donnes sont
crites dans la base, le fournisseur incrmente le numro de version 2.
Si tx2 augmente le prix de 5$ et valide galement cette modification, le
gestionnaire dentits de tx2 ralisera que le numro de version dans la base est
diffrent de celui de lentit, ce qui signifie que lentit a t modifie, une
exception OptimisticLockException sera alors lance.

2014/2015 Java Entreprise Edition : JEE 7 81

JPQL : verrouillage optimiste


On peut contrler lendroit de placement de verrou optimiste en
choisissant une stratgie lire puis verrouiller ou lire et verrouiller.
Book book = em.find(Book.class, 12);
//verrouillage pour augmenter le prix
em.lock(book, LockModeType.OPTIMISTIC);
em.raisePriceByTwoDollars();

Avec le verrouillage optimiste, la valeur de LokModeType est


OPTIMISTIC ou OPTIMISTIC_FORCE_INCREMENT ( force une
incrmentation de la version de lentit) . Il y aussi READ et WRITE sont
dprcies.

Il est fortement conseill dutiliser le verrouillage optimiste pour les entits


ayant un accs concurrent. Aussi, il est prfrable dutiliser le verrou qui
donne de meilleures performances.

2014/2015 Java Entreprise Edition : JEE 7 82

41
JPQL : verrouillage pessimiste
Consiste verrouiller systmatiquement lentit avant de la manipuler.
Ce mcanisme est trs restrictif et dgrade les performances de faon
significative puisquil implique que la base pose un verrou avec SELECT
FOR UPDATE lorsquelle lit les donnes.
Cest un mcanisme efficace pour garantir que deux clients ne modifient pas la
mme ligne en mme temps, mais il exige des vrifications de bas niveau qui
pnalisent les performances.
Les transactions qui violent cette contrainte provoquent la leve dune exception
PessimisticLockException et sont annules.
Si 100 millions de personnes veulent vendre leurs actions en mme temps, le
systme doit utiliser un verrouillage pessimiste pour assurer la cohrence des
donnes.
Le verrouillage pessimiste peut sappliquer aux entits qui ne sont pas annots
par @Version.

2014/2015 Java Entreprise Edition : JEE 7 83

Cycle de vie dune entit


JPA permet de greffer du code mtier au cycle de vie dune entit ayant des
vnements : ce code est ensuite automatiquement appel par le fournisseur de
persistance laide de mthodes de rappel.
Les mthodes de rappel et les couteurs peuvent tre vus comme les triggers
dune base de donnes relationnelle.
Un trigger excute du code mtier pour chaque ligne dune table alors que les
mthodes de rappel et couteurs sont appels sur chaque instance dune entit
en rponse un vnement ou, plus prcisment, avant et aprs la survenue
dun vnement.
Pour dfinir ces mthodes Pre et Post, on peut utiliser des annotations ou
des descripteurs XML.
Lorsquune entit est cre ou rendue persistante par le gestionnaire dentits,
celle-ci est dite gre et le gestionnaire synchronise automatiquement la valeur
de ses attributs avec la base correspondante.

2014/2015 Java Entreprise Edition : JEE 7 84

42
Cycle de
vie dune
entit

Examinons la figure ci-dessus, qui reprsente les tats que peut prendre une
entit Customer, ainsi que les transitions entre ces tats.
Lorsquon cre une instance de lentit Customer laide de new. Elle existe en
mmoire bien que JPA ne la connaisse pas.
Si lon nen fait rien, elle devient hors porte et finit par tre supprime par le
garbage, ce qui marque la fin de son cycle.

2014/2015 Java Entreprise Edition : JEE 7 85

Cycle de vie dune entit


On peut aussi rendre persistant linstance de Customer avec em.persist(),
auquel cas lentit devient gre et son tat est synchronis avec la base.
Pendant quelle est dans cet tat, on peut modifier ses attributs avec setters
(customer.seFirstName()) ou rafrachir son contenu avec em.refresh().
Toutes ces modifications garderont lentit synchronise avec la base. Si lon
appelle la mthode em.contains(customer), celle-ci renverra true car customer
appartient au contexte de persistance (elle est gre).
Un autre moyen de grer une entit consiste la charger partir de la base
avec em.find() ou dune requte JPQL rcuprant une liste dentits qui sont
toutes automatiquement gres.
Dans un tat gr, em.remove() supprime lentit de la base et elle nest plus
gre. Cependant, lobjet java continue dexister en mmoire, et il reste utilisable
tant que le ramasse-miettes (garbage) ne le supprime pas.
em.clear() supprime lentit du contexte de persistance et devient alors
dtache.

2014/2015 Java Entreprise Edition : JEE 7 86

43
Cycle de vie dune entit
Un autre moyen de dtacher une entit, cest de la srialiser. En effet, les
entits doivent implmenter linterface Serializable pour passer par un rseau
afin dtre invoques distance ou pour traverser des couches afin de safficher
dans une couche prsentation-cette restriction est due java et non JPA.
Une entit srialise, qui passe par le rseau et est dsrialise comme un
objet dtach : pour la rattacher, il faut appeler em.merge().
Le cycle de vie dune entit se dcompose en quatre parties : persistance,
modification, suppression et chargement, qui correspondent aux oprations
quivalentes sur la base.
Chacune de ces parties est associe un vnement Pr et Post qui peut
tre intercept par le gestionnaire dentits pour appeler une mthode mtier qui
doit avoir t marque par lune des annotations suivantes :

2014/2015 Java Entreprise Edition : JEE 7 87

Mthodes de rappel
Annotation Description
@PrePersist La mthode est appele avant lexcution em.persist()
@PostPersist La mthode est appele aprs que lentit soit devenue persistante. Si
lentit produit sa cl primaire (avec @GeneratedValue), sa valeur est
accessible dans la mthode
@PreUpdate La mthode est appele avant une opration de modification de lentit
dans la base (appel setters de lentit ou em.merge())
@PostUpdate La mthode est appele aprs une opration de modification de lentit
dans la base
@PreRemove La mthode est appele avant lexcution de em.remove()
@PostRemove La mthode est appele aprs la suppression de lentit
@PostLoad La mthode est appele aprs le chargement de lentit (par JPQL ou
em.find()) ou avant quelle soit rafrachie partir de la base. Il nexiste pas
dannotation @PreLoad car cela naurait aucun sens dagir sur une entit
qui na pas encore t construite.

2014/2015 Java Entreprise Edition : JEE 7 88

44
Mthodes
de rappel

Avant dinsrer une entit dans la base, le gestionnaire dentits appelle la mthode
annote par @PrePersist. Si linsertion ne provoque pas dexception, lentit est rendue
persistante, son identifiant est cre, puis la mthode annote par @PostPersist est
appele.
Il en va de mme pour les mises jour (@PreUpdate et @PostUpdate) et les suppressions
(@PreRemove et @PostRemove).
Lorsquune entit est charge de la base (par em.find() ou JPQL), la mthode est annote
par @PostLoad est appele. Lorsque lentit dtache a besoin dtre fusionne, le
gestionnaire dentits doit dabord vrifier si la version en mmoire est diffrente de celle en
base (@PostLoad) et modifier les donnes (@PreUpdate et @PostUpdate) si cest le cas.

2014/2015 Java Entreprise Edition : JEE 7 89

Mthodes de rappel
Outre les attributs, les constructeurs, les getters et les setters, les entits
peuvent contenir du code mtier pour valider leur tat ou calculer certains
attributs.
Ce code mtier peut tre plac dans des mthodes java classiques invoques
par dautres classes ou dans des mthodes de rappel (callbacks).
Dans le cas des mthodes de rappel, cest le gestionnaire dentits qui les
appelle automatiquement en fonction de lvnement qui t dclench.
Exemple : soit lentit Customer dfinit une mthode pour valider les donnes.
Elle vrifie les valeurs des attributs dateOfBirth et phoneNumber.
Cette mthode est annote par @PrePersist et @PreUpdate, elle sera appele
avant linsertion ou la modification des donnes dans la base.
Si ces donnes ne sont pas valides, la mthode lvera une exception
lexcution et linsertion ou la modification sera annule.

2014/2015 Java Entreprise Edition : JEE 7 90

45
@Entity
public class Customer { @Id @GeneratedValue private Long id;
private String firstName; private String lastName; private String email;
private String phoneNumber;
@Temporal(TemporalType.DATE) private Date dateOfBirth;
@Transient private Integer age;
@Temporal(TemporalType.TIMESTAMP) private Date creationDate;
@PrePersist @PreUpdate
private void validate() {
if (dateOfBirth.getTime()>new Date().getTime() )
throw new IllegalArgumentException("Invalid date of birth");
if (!phoneNumber.startsWith("+"))
throw new IllegalArgumentException("Invalid phone number"); }
@PostLoad @PostPersist @PostUpdate
public void calculateAge() { if (dateOfBirth == null) { age = null; return; }
Calendar birth = new GregorianCalendar(); birth.setTime(dateOfBirth);
Calendar now = new GregorianCalendar(); now.setTime(new Date());
int adjust = 0;
if (now.get(DAY_OF_YEAR) - birth.get(DAY_OF_YEAR) < 0) { adjust = -1; }
age = now.get(Calendar.YEAR) - birth.get(Calendar.YEAR) + adjust; }
// Constructeurs, getters et setters
}
2014/2015 Java Entreprise Edition : JEE 7 91

Mthodes de rappel
calculateAge() calcule lge du client. Lattribut age est transitoire (nest pas crit
dans la base) : lorsque lentit est charge, rendue persistante ou modifie,
cette mthode calcule lge partir de la date de naissance et initialise lattribut.
Les mthodes de rappel doivent respecter les rgles suivantes :
Elles peuvent avoir un accs public, priv, protg ou paquetage, mais pas
statiques ni finales.
Elles peuvent tre marques par plusieurs annotations du cycle de vie.
Cependant, cet annotation ne peut apparatre quune seule fois dans une entit
(par exemple, pas deux fois une @PrePersist).
Elles peuvent lancer des exceptions non contrles mais pas dexceptions
contrles (si exception la transaction en cours est annule).
Elles peuvent invoquer JNDI, JDBC, JMS et les EJB, mais aucune opration
EntityQuery ou Query.
Avec lhritage, si une mthode de rappel est dfinie dans la superclasse, elle
sera appele avant la mthode de rappel de la classe fille.
2014/2015 Java Entreprise Edition : JEE 7 92

46
Mthodes de rappel
Si une relation utilise la rpercussion des vnements, la mthode de rappel
associe sera galement appele en cascade. Exemple : si Customer contient
une collection dadresses et que la suppression dun Customer soit rpercute
sur Address, la suppression dun client invoquera la mthode @PreRemove
dAddress et celle de Customer.

Les mthodes de rappel dune entit fonctionnent bien lorsque la logique mtier
nest lie qu cette entit.
Les couteurs (listeners) permettent dextraire cette logique dans une classe
spare qui pourra tre partage par plusieurs entits.
Un couteur dentit est simplement un POJO qui dfinit une ou plusieurs
mthodes de rappel du cycle de vie. Pour enregistrer un couteur, il suffit que
lentit utilise lannotation @EntityListeners.
Par rapport lexemple prcdent, on va extraire les mthodes calculateAge() et
validate() pour les placer respectivement dans deux classes couteurs,
AgeCalculationListener et DataValidationListener suivantes :
2014/2015 Java Entreprise Edition : JEE 7 93

couteurs (listeners)
public class AgeCalculationListener {
@PostLoad @PostPersist @PostUpdate
public void calculateAge(Customer customer) {
System.out.println("AgeCalculationListener calculateAge()");
if (customer.getDateOfBirth() == null) { customer.setAge(null); return; }
Calendar birth = new GregorianCalendar(); birth.setTime(customer.getDateOfBirth());
Calendar now = new GregorianCalendar(); now.setTime(new Date());
int adjust = 0;
if (now.get(DAY_OF_YEAR) - birth.get(DAY_OF_YEAR) < 0) { adjust = -1; }
customer.setAge(now.get(YEAR) - birth.get(YEAR) + adjust);
} }
public class DataValidationListener {
@PrePersist @PreUpdate
private void validate(Customer customer) {
if (dateOfBirth.getTime()>new Date().getTime() )
throw new IllegalArgumentException("Invalid date of birth");
if (!phoneNumber.startsWith("+"))
throw new IllegalArgumentException("Invalid phone number"); }
}
2014/2015 Java Entreprise Edition : JEE 7 94

47
couteurs (listeners)
Une classe couteur ne doit obir qu des rgles simples :
Elle doit avoir un constructeur public sans paramtre.
Les mthodes de rappel qui y sont dfinies ont deux signatures :
Si une mthode doit servir plusieurs entits, elle doit prendre un paramtre de
type Object. void <Methode> (Object uneEntity) .
Si elle nest destine qu une seule entit ou ses sous-classes, le paramtre
peut tre celui de lentit. void <Methode>(Customer customerOuSousClasse).
Pour indiquer que ces deux classes couteurs seront utilises comme des
vnements du cycle de vie de lentit Customer.
Lentit Customer doit prciser ceci avec lannotation @EntityListeners. Cette
annotation prend en paramtre une classe couteur ou un tableau dcouteurs.
Lorsquil y a plusieurs couteurs et quun vnement du cycle de vie survient, le
fournisseur de persistance parcourt chacun de ces couteurs dans lordre o ils
ont t indiqus et invoquera la mthode de rappel en lui passant une rfrence
lentit concerne par lvnement.

2014/2015 Java Entreprise Edition : JEE 7 95

couteurs (listeners)
Puis il appellera les mthodes de rappel de lentit elle-mme (sil y en a).
@EntityListeners({DataValidationListener.class, AgeCalculationListener.class})
@Entity
public class Customer { @Id @GeneratedValue private Long id;
private String firstName; private String lastName; private String email;
private String phoneNumber;
@Temporal(TemporalType.DATE) private Date dateOfBirth;
@Transient private Integer age;
@Temporal(TemporalType.TIMESTAMP) private Date creationDate;
// Constructeurs , getters et setters
}
Ce code produit le mme rsultat que le code de la page 91.
Lentit Customer utilise la mthode DataValidationlistener.validate() pour
valider ses donnes avant toute insertion ou mise jour et la mthode
AgecalculationListener.calculateAge() pour calculer son ge.

2014/2015 Java Entreprise Edition : JEE 7 96

48
couteurs (listeners)
Les rgles que doivent respecter les mthodes dun couteur sont les mmes
que celles suivies par les mthodes de rappel, mis part quelques dtails.
Elles ne peuvent lancer que des exceptions non contrles. C.--d. les autres
couteurs et mthodes de rappel ne seront pas appels et que lventuelle
transaction sera annule.
Dans une hirarchie de classes, si plusieurs entits dfinissent des couteurs,
ceux de la superclasse seront appels avant ceux des sous-classes. Si une
entit ne veut pas hriter des couteurs de sa superclasse, elle peut
explicitement les exclure laide dune annotation
@ExludeSuperclassListeners (ou son quivalent en XML).
Lentit Customer de la page prcdente dfinit deux couteurs, mais il est
possible quun couteur soit dfini par plusieurs entits.
Le code de la page suivante, cre un couteur de dbogage affichant le nom
des vnement dclenchs.

2014/2015 Java Entreprise Edition : JEE 7 97

couteurs (listeners)
public class DebugListener {
@PrePersist
void prePersist(Object object) { System.out.println("++ prePersist"); }
@PreUpdate
void preUpdate(Object object) { System.out.println("++ preUpdate"); }
@PreRemove
void preRemove(Object object) { System.out.println("++ preRemove"); }
@PostLoad
void postLoad(Object object) { System.out.println("++ postLoad"); }
@PostRemove
void postRemove(Object object) { System.out.println("++ postRemove"); }
@PostUpdate
void postUpdate(Object object) { System.out.println("++ postUpdate"); }
@PostPersist
void postPersist(Object object) { System.out.println("++ PostPersist"); }
}
2014/2015 Java Entreprise Edition : JEE 7 98

49
couteurs (listeners)
Chaque mthode du code prcdent prend un Object en paramtre, ce qui signifie
que nimporte quel type dentit peut utiliser cet couteur en ajoutant la classe
DebugListener son annotation @EntityListeners.
Pour viter dajouter manuellement cette annotation chacune des entits de
lapplication, JPA permet de dfinir des couteurs par dfaut qui couvrent toutes
les entits dune unit de persistance.
Or il nexiste pas dannotation ayant la porte dune unit de persistance, cest
pourquoi ces couteurs par dfaut sont dclars dans un fichier dassociation
XML.
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" version="2.0">
<persistence-unit-metadata> <persistence-unit-defaults>
<entity-listeners> <entity class="com.apress.javaee7.DebugListener">
</entity-listeners>
</persistence-unit-defaults> </persistence-unit-metadata>
</entity-mappings >
2014/2015 Java Entreprise Edition : JEE 7 99

couteurs (listeners)
Le marquer <persistence-unit-metadata> sert dfinir toutes les mtadonnes
qui nont pas dquivalent avec les annotations.
Le marquer <persistence-unit-defaults> dfinit toutes les valeurs par dfaut de
lunit de persistance.
<entity-listener> dfinit lcouteur par dfaut.
Ce fichier doit tre nomm persistence.xml et doit tre dploy avec
lapplication. DebugListener sera alors automatiquement appele par toutes les
entits.
Si on dfinit une liste dcouteurs par dfaut, chacun deux sera appel dans
lordre o il apparat dans le fichier XML.
Les couteurs par dfaut sont toujours invoqus avant ceux dfinis par
lannotation @EntityListeners. Pour quils ne sappliquent pas une entit
particulire, celle-ci doit le prciser avec lannotation
@ExcludeDefaultListeners.

2014/2015 Java Entreprise Edition : JEE 7 100

50
couteurs (listeners)
@ExcludeDefaultListeners
@Entity
public class Customer {
@Id @GeneratedValue private Long id;
private String firstName;
private String lastName;
private String email;
private String phoneNumber;
@Temporal(TemporalType.DATE) private Date dateOfBirth;
@Transient private Integer age;
@Temporal(TemporalType.TIMESTAMP) private Date creationDate;
// Constructeurs, getters et setters
}

2014/2015 Java Entreprise Edition : JEE 7 101

51