Vous êtes sur la page 1sur 173

INTRODUCTION À JPA

(JAVA PERSISTENCE API)


Introduction
– La plupart des applications manipulent des
données qui sont stockées dans des bases
de données relationnelles
– Les données dans les bases de données
sont : orientées données, des tuples et des
colonnes
– Les applications (OO) travaillent avec les
classes et les objets

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 2 / 173


Introduction
– Les bases de données (normalisation) sont
optimisées pour le stockage
– La conception orientée objet est optimisée
pour la maintenabilité et la lisibilité
– Il arrive des fois où les deux sont en conflit
et il résulte une inadéquation entre les deux
ce qui rend la tâche de la persistance
difficile

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 3 / 173


Introduction
L’inadéquation est due à plusieurs facteurs :
– Granularité : l’on travaille plus ou moins avec les classes au
lieu des tables
– Héritage : pas très bien représenté dans les bases de données
– Associations : unidirectionnelles en Java alors que dans les
bases de données l’on parle de clés étrangères
– Multiplicité des associations : non spécifiée en Java alors que
c’est explicite avec les clés étrangères
– Navigation des données : l’on traverse les objets en Java alors
que l’on utilise les jointures avec les bases de données.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 4 / 173


ORM / JPA
– Pour répondre cette inadéquation, l’on a introduit une
couche appelée ORM (Object Relational Mapping).
– Elle sert d’intermédiaire entre le monde objet et celui
relationnel.
– JPA est une spécification pour un ORM et persistance en
Java
– Hibernate, EclipseLink, OpenJPA et d’autres fournissent
des implémentations pour la spécification JPA
– L’on utilise les annotations pour faire la correspondance
entre les objets et propriétés java et les tables et
colonnes
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 5 / 173
ORM / JPA - Bénéfices
– L’on n’a pas vraiment besoin de connaître SQL pour
interagir avec une base de données relationnelle
– L’on a un modèle consistant pour interagir avec les
bases de données
– L’indépendance vis à vis du SGBD ou de
l’implémentation JPA (si l’on n’utilise pas les
fonctionnalités spécifiques à un SGBD ou du
fournisseur JPA)
– Souvent l’on écrit moins de code

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 6 / 173


ORM / JPA Inconvénients
– Complexité : JPA ajoute une couche
d’abstraction qui peut être :
» difficile à apprendre ;
» Difficile à déboguer ;
» Difficile à optimiser.
– Manque de flexibilité : il n’est pas toujours
possible de tirer profit des fonctionnalités
spécifiques à une implémentation.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 7 / 173


Example

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 8 / 173


Les composants de JPA
JPA est constitué des éléments suivants :
– L’API JPA ;
– Un langage de requête ;
– L’API Criteria pour les requêtes
– Un metadata pour la correspondance entre
objet et relationnel

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 9 / 173


Les entités
– Une entité désigne un objet du domaine qui
doit être persisté.
– Typiquement, une entité représente une
table dans le modèle relationnel.
– Les instances d’une entités représentent les
lignes (tuples) dans les tables.
– Généralement, une entité est représentée
par une classe.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 10 / 173


Les entités
Il y’a un certain nombre de contraintes qui doivent être
remplie pour qu’une classe soit une entité :
– La classe doit être annotée avec l’annotation
javax.persistence.Entity.
– La classe doit avoir un attribut annoté avec
javax.persistence.Id.
– La classe doit avoir un constructeur public ou protected qui
n’accepte aucun argument.
– La classe ne doit pas être déclarée final.
– Une entité qui est passée par valeur, comme objet détaché
doit implémenter l’interface java.io.Serializable.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 11 / 173
Les champs persistants
Les champs persistants (attributs qui
correspondent à des colonnes des tables)
doivent être d’un des types suivants :
– Les types primitifs Java ou java.lang.String ;
– Les autres types sérialisables (comme byte[],
java.util.Date, …) ;
– Les types enumérés ;
– D’autres entités ou collections d’entités ;
– Les classes imbriquées ;
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 12 / 173
Les champs persistants
– Une entité persistante doit suivre la
convention JavaBeans (les champs sont
déclarés private et l’on définit les getters et
setters pour ces champs) ;
– Par défaut, tout attribut d’une entités est un
champ persistant sauf s’il est déclaré
transient ou annoté avec
javax.persistence.Transient.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 13 / 173


Les champs persistants
– Il existe un ensemble de valeurs par défauts pour
les champs persistants.
– L’on peut cependant un contrôle sur ces champs en
utilisant les annotations javax.persistence.Column
et javax.persistence.Basic.
– Javax.persistence.Basic définit le champ de l’objet
alors que javax.persistence.Column est utilisée
pour contrôler les propriétés de la colonne de la
table (si l’on génère automatiquement le schéma).

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 14 / 173


L’annotation Basic
– Cette annotation définit les propriétés d’un
champ persistant.
– Basic accepte deux paramètres optionnels :
» Optional (boolean) qui dit si la valeur du champ
est optionnelle ou pas.
» Fetch (FetchType) qui définit si la valeur du
champ doit être chargée à la demande (de façon
paresseuse) ou de manière agressive.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 15 / 173


L’annotation Column
– Cette annotation sert à définir les propriétés de la colonne qui
correspond au champ persistent :
– Elle accepte les paramètres suivants :
» name (String) qui définit le nom de la colonne ;
» nullable (boolean) qui spécifie si la colonne accepte la valeur nulle ;
» length (int) : la longueur de la colonne ;
» unique (boolean) spécifie si la valeur est unique ;
» columnDefinition (String) un fragment SQL qui sera utilisé lors de la
génération du schéma ;
» insertable (boolean) : la colonne doit elle être incluse lors des requêtes
d’insertion ;
» updatable(boolean) : la colonne doit elle être incluse lors des requêtes de
mises à jour ;
» table (String) spécifie le nom de la table.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 16 / 173


Annotation supplémentaire
» Il existe une annotation spéciale qui permet de
contrôler les champs dont le type est une date :
javax.persistence.Temporal :
» Elle accepte un seul paramètre value de type
TemporalType dont la valeur peut être:
› DATE  correspond à java.sql.Date
› TIME  correspond à java.sql.Time
› TIMESTAMP correspond à java.sql.Timestamp

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 17 / 173


Entité Embeddable

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 18 / 173


La clé primaire
Chaque entité doit déclarer une clé primaire en utilisant
l’annotation javax.persistence.Id sur l’un des champs
persistants.
En principe la valeur de cet attribut ne doit jamais changer.
Pour une clé composée, l’on va utiliser
javax.persistence.IdClass ou javax.persistence.EmbeddedId
Le type de la clé primaire peut être :
» Un type primitif ou l’enveloppe d’un type primitif ;
» lava.lang.String ;
» java. util.Date
» java.sql.Date

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 19 / 173


La clé primaire
Il est possible de générer automatiquement la clé
primaire.
Si la clé est numérique :
» @GeneratedValue indique que la clé sera automatiquement
générée par le SGBD ;
» Cette annotation peut avoir un paramètre strategy qui
indique comment la clé sera générée ;
› AUTO : le SGBD choisit (valeur par défaut) ;
› SEQUENCE : il utilise une séquence SQL ;
› IDENTITY : il utilise un générateur de type IDENTITE (autoincrement
avec MySQL par exemple)
› TABLE : il utilise une table qui contient la prochaine valeur.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 20 / 173
Clé composée
Relation existante avec une clé multi-attributs ;
Relation avec association M – N ;
L’on a deux possibilités :
» @IdClass correspond à plusieurs attributs @Id dans la classe
» @EmbeddedId et @Embeddable : un seul attribut @Id dans la classe
Dans les deux cas, la clé doit être représentée par une classe
java :
» Les attributs correspondent aux composantes de la clé
» La classe doit être publique
» La classe doit avoir un constructeur sans argument ;
» La classe doit être serialisable et redéfinir les méthodes equals et
hashcode
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 21 / 173
@EmbeddedId
Correspond au cas où l’entité a une seule clé (clé
composée)
La classe de la clé primaire doit être annotée avec
javax.persistence.Embeddable
@Entity
@Entity @Embeddable
@Embeddable
public
public class
class Client
Client implements
implements Serializable{
Serializable{ public
public class
class ClientPK
ClientPK implements
implements Serializable{
Serializable{

@EmbeddedId
@EmbeddedId private
private String
String nom;
nom;
private
private ClientPK
ClientPK clientPK;
clientPK;
@Temporal(TemporalType.DATE)
@Temporal(TemporalType.DATE)
private
private int
int age;
age; private
private Date
Date dateDeNaissance;
dateDeNaissance;

… ...
...

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 22 / 173


@IdClass
Utilisée lorsque l’on utilise plusieurs @Id
La classe entité est annotée @IdClass qui prend en
paramètre la classe clé primaire ;
La classe clé primaire n’est pas annotée :
» Ses attributs ont les mêmes noms et les mêmes types que les attributs
annotés @Id dans la classe entité
@Entity
@Entity
@IdClass(ClientPK.class)
@IdClass(ClientPK.class)
public
public class
class Client
Client implements
implements Serializable{
Serializable{ public
public class
class ClientPK
ClientPK implements
implements Serializable{
Serializable{

@Id
@Id private
private String
String nom;
nom; private
private String
String nom;
nom;

@Id
@Id @Temporal(TemporalType.DATE)
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
@Temporal(TemporalType.DATE)
private
private Date
Date dateDeNaissance;
dateDeNaissance; private
private Date
Date dateDeNaissance;
dateDeNaissance;
...
...
private
private int
int age;
age;

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 23 / 173


Le cycle de vie des entités

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 24 / 173


Personnaliser la création de la
table

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 25 / 173


Gestion de l’héritage
– Les entités supportent l’héritage des
classes, les associations et requêtes
polymorphiques.
– Les classes entités peuvent étendre une
classe simple (non entité) et une classe
simple peut hériter d’une classe entité.
– Les classes entités peuvent être abstraites
ou concrètes

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 26 / 173


Les entités abstraites
– Une classe abstraite peut être déclarée comme
une entité en l’annotant avec @Entity.
– Une entité abstraite est comme tout autre
entité à la différence qu’elle ne peut être
instanciée.
– Elle peut faire l’objet d’une requête. Si elle est
la cible d’une requête, alors la requête portera
sur toutes les sous-classes concrètes.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 27 / 173


Les superclasses mappées
– Les classes entités peuvent hériter des superclasses
qui contiennent des champs persistants et des
informations de mapping mais ne sont pas des entités.
– Ces superclasses sont généralement utilisées lorsque
les informations de mapping sont communes à
plusieurs classes.
– Ces classes sont décorées avec l’annotation
javax.persistence.MappedSuperclass.
– Ces classes ne peuvent pas faire l’objet d’une requête
et ne sont pas utilisées par le EntityManager.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 28 / 173


Les superclasses mappées

@MappedSuperclass
@MappedSuperclass
public
public class
class Employee
Employee {{
@Id
@Id
protected
protected Integer
Integer employeeId;
employeeId;
...
...
}}
@Entity
@Entity
public
public class
class FullTimeEmployee
FullTimeEmployee extends
extends Employee
Employee {{
protected
protected Integer
Integer salary;
salary;
...
...
}}
@Entity
@Entity
public
public class
class PartTimeEmployee
PartTimeEmployee extends
extends Employee
Employee {{
protected
protected Float
Float hourlyWage;
hourlyWage;
...
...
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 29 / 173


La gestion de l’héritage
– Vous pouvez configurer la façon dont le fournisseur
JPA fait la correspondance entre les classes héritées
et la base de données en utilisant l’annotation
javax.persistence.Inheritance.
– Il existe trois façons de faire la correspondance entre
les sous classes et les tables :
» Une seule table par hiérarchie de classe
» Une table par entité concrète
» La jointure où les propriétés propres à une classe concrète
sont dans une table différente de la table qui contient les
propriétés communes.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 30 / 173
La gestion de l’héritage
La stratégie est configurée en spécifiant la
propriété strategy de @Inheritance à l’une des
valeurs de InheritanceType :
» SINGLE_TABLE
» JOINED
» TABLE_PER_CLASS
La stratégie par défaut,
InheritanceType.SINGLE_TABLE, est utilisée si
l’annotation @Inheritance n’est pas spécifiée sur
la classe mère.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 31 / 173
La gestion de l’héritage
Nous allons considérer la hiérarchie des
classes suivantes :

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 32 / 173


La stratégie - SINGLE_TABLE
– Toutes les classes de la hiérarchie correspondent
à une seule table dans la base de données.
– Cette table a une colonne comme discriminant
qui contient une valeur qui permet d’identifier la
sous-classe correspondante.
– La colonne discriminante peut être spécifiée par
l’annotation
javax.persistence.DiscriminatorColumn au niveau
de la classe mère.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 33 / 173


La stratégie - SINGLE_TABLE
DiscriminatorColumn accepte les paramètres
suivants :
» name (String) désigne le nom de la colonne
discriminante ;
» discriminatorType (DiscriminatorType) définit le type
de la colonne discriminante ;
» columnDefinition (String) est un fragment de SQL qui
doit être utilisé lors de la création de la colonne
discriminante ;
» length (String) la longueur de la colonne discriminante
si elle est de type String.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 34 / 173
La stratégie - SINGLE_TABLE
Javax.persistence.DiscriminatorType est définit comme suit :
public enum DiscriminatorType {
STRING,
CHAR,
INTEGER
};
Si @DiscriminatorColumn n’est pas spécifié, alors le nom de
la colonne est DTYPE de type DiscriminatorType.STRING.
Les classes concrètes peuvent utiliser l’annotation
javax.persistence.DiscriminatorValue pour spécifier la valeur
de la colonne discriminante pour cette classe.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 35 / 173


La stratégie - SINGLE_TABLE
@Entity
@Entity
@Inheritance(strategy
@Inheritance(strategy == InheritanceType.SINGLE_TABLE)
InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name
@DiscriminatorColumn(name == "TPersonne",
"TPersonne", discriminatorType
discriminatorType == DiscriminatorType.STRING)
DiscriminatorType.STRING)
public
public class
class Personne
Personne implements
implements Serializable{
Serializable{
@Id
@Id @GeneratedValue(strategy
@GeneratedValue(strategy == GenerationType.AUTO
GenerationType.AUTO private
private Long
Long id;
id;
@Column
@Column private
private String
String nom;
nom;
@Column private String prenom;
@Column private String prenom;
@Column
@Column @Temporal(TemporalType.DATE)
@Temporal(TemporalType.DATE) private private Date
Date dateDeNaissance;
dateDeNaissance;
….
….

@Entity
@Entity @Entity
@DiscriminatorValue(value @Entity
@DiscriminatorValue(value == "Etudiant")
"Etudiant") @DiscriminatorValue(value
@DiscriminatorValue(value == "Enseignant")
"Enseignant")
public
public class
class Etudiant
Etudiant extends
extends Personne{
Personne{ public
@Column public class
class Enseignant
Enseignant extends
extends Personne{
Personne{
@Column private
private String
String matricule;
matricule; @Column
@Column private
private String
String specialite;
specialite;
@Column
@Column private
private String
String niveau;
niveau; ….
…. ….
….

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 36 / 173


La stratégie TABLE_PER_CLASS
Chaque classe concrète correspond à une
table différente dans la base de données.
Tous les champs dans la classe, y compris les
champs hérités, correspondent à des
colonnes dans la table correspondante à la
classe.
Le support pour cette stratégie est
optionnelle et certaines implémentations ne
la supporte pas.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 37 / 173
La stratégie TABLE_PER_CLASS
@Entity
@Entity @Inheritance(strategy
@Inheritance(strategy == InheritanceType.TABLE_PER_CLASS)
InheritanceType.TABLE_PER_CLASS)
public
public abstract class Personne implements Serializable{
abstract class Personne implements Serializable{


@Entity
@Entity
public
public class
class Etudiant
Etudiant extends
extends Personne{
Personne{


@Entity
@Entity
public
public class
class Enseignant
Enseignant extends
extends Personne{
Personne{

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 38 / 173


La stratégie - JOINED
Dans cette stratégie, la classe de base est
représentée par une table et chaque classe
concrète a une table qui ne contient que les
propriétés propres à cette classe.
– La table d’une sous classe contient une
colonne ou des colonnes qui correspond à sa
clé primaire et est une clé étrangère pour la
clé primaire de la table de la superclasse.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 39 / 173


La stratégie - JOINED
@Entity
@Entity @Inheritance(strategy
@Inheritance(strategy == InheritanceType.JOINED)
InheritanceType.JOINED)
public
public abstract class Personne implements Serializable{
abstract class Personne implements Serializable{


@Entity
@Entity
public
public class
class Etudiant
Etudiant extends
extends Personne{
Personne{


@Entity
@Entity
public
public class
class Enseignant
Enseignant extends
extends Personne{
Personne{

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 40 / 173


Gestion de l’héritage
Les différentes stratégies ont des avantages et des
inconvénients :
– La stratégie SINGLE_TABLE fournit un bon support pour les
relations polymorphiques entre les entités et les requêtes qui
traversent toute la hiérarchie. Cependant, les attributs propres
aux classes concrètes ne doivent pas être nuls.
– La stratégie TABLE_PER_CLASS est optionnelle et offre un
mauvais support pour les relations polymorphiques et nécessite
souvent l’usage de union ou d’autres requêtes SQL pour parcourir
toute la hiérarchie.
– La stratégie JOINED offre un bon support pour les requêtes
polymorphiques mais nécessite l’utilisation d’une jointure pour
lorsque l’on instancie une classe concrète.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 41 / 173
Les associations
Les associations définissent les relations entre les
entités.
Une association peut être uni-directionnelle ou
bidirectionnelle
Les cardinalités sont : 1 – 1, 1 – M, N – 1, N – M
Elles sont définies grâce à une annotation sur la
propriété correspondante
//// Associations
Associations entre
entre Client
Client et
et Commandes
Commandes
@OneToMany
@OneToMany
private
private Collection<Commande>
Collection<Commande> commandes ;
commandes ;

L’on a le support de Collection, Set, List et Map


Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 42 / 173
Les associations bidirectionnelles
Le développeur est responsable de la gestion des deux
bouts de l’association.
Un bout est le propriétaire de l’association : Il détermine
comment les mises à jour de l’association sont opérées.
Propriétaire :
» Pour les associations autre que M – N, ce bout correspond à la
table qui contient la clé étrangère qui traduit l’association ;
» Pour les associations M-N, le développeur choisit le bout
propriétaire
» Le bout non propriétaire est qualifié par l’attribut mappedBy qui
donne le nom de l’association correspondante dans le bout
propriétaire.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 43 / 173
Association 1 - 1
Annotation @OneToOne
Représente une clé étrangère dans une table
@Entity
@Entity
public
public class
class Personne
Personne {{
……
@OneToOne
@OneToOne
private
private Adresse
Adresse adresse ;
adresse ;
...
...
}}

Accepte un paramètre optional (boolean) qui


indique si l’association est obligatoire ou pas

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 44 / 173


Associations 1 – N et N - 1
Annotations @OneToMany et @ManyToOne
Elle est représentée par une clé étrangère dans la table qui
correspond au côté propriétaire (obligatoirement le côté Many)
@Entity
@Entity @Entity
@Entity
public
public class
class Client{
Client{ public
public class
class Commande{
Commande{
@Id
@Id private int
private int id;
id; @Id
@Id private int
private int id;
id;
…… ……
@OneToMany
@OneToMany (mappedBy="c")
(mappedBy="c") @ManyToOne
@ManyToOne
private
private Set<Commande>
Set<Commande> commande ;
commande ; private
private Client
Client c ;
c ;
...
... ...
...
}} }}

Commande
Commande
Client
Client id
id
id
id clientId
clientId
...
... ...
...
45

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 45 / 173


L’annotation @JoinColumn
Avec les annotations @OneToOne, @ManyToOne
et @ManyToOne, il est possible redéfinir la colonne
clé étrangère grâce à l’annotation @JoinColumn.
@JoinColumn prend les mêmes attributs que
@Column.
@Entity
@Entity
public
public class
class Commande{
Commande{
@Id
@Id private
private int
int id;
id;
……
@ManyToOne
@ManyToOne
@JoinColumn(name=
@JoinColumn(name=""C_ID C_ID""))
private
private Client
Client c ;
c ;
...
...
}} 45

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 46 / 173


Associations M - N
Annotation @ManyToMany
Elle est représentée par une table d’association
Les valeurs par défauts (que l’on peut modifier) :
» Le nom de la table d’association est la concaténation
des noms des deux tables séparés par _
» Les noms des attributs clés étrangères sont la
concaténation de la table référencée, _, et de
l’attribut id de la table référencée.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 47 / 173


Associations M - N
@Entity
@Entity @Entity
@Entity
public
public class
class Personne{
Personne{ public
public class
class Adresse{
Adresse{
@Id
@Id @Id
@Id
private
private int
int id ;
id ; private
private int
int id ;
id ;
@ManyToMany
@ManyToMany @ManyToMany(mappedBy="adresses")
@ManyToMany(mappedBy="adresses")
private
private Set<Adresse>
Set<Adresse> adresses ;
adresses ; private
private Set<Personne>
Set<Personne> personnes ;
personnes ;
...
... …

}} }}

Personne
Personne Adresse
Adresse
id
id id
id
.... ....

Personne_Adresse
Personne_Adresse
personnes_id
personnes_id
adresses_id
adresses_id

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 48 / 173


Associations M – N
Si le mapping par défaut ne convient pas, l’on le
surcharger avec l’annotation @JoinTable ;
Le côté non propriétaire est toujours spécifié grâce à
mappedBy ;
@Entity
@Entity
public
public class
class Personne
Personne {{
@Id
@Id
private
private int
int id;
id;

@ManyToMany
@ManyToMany
@JoinTable(name="Personne_Adresse",
@JoinTable(name="Personne_Adresse",
JoinColoumns
JoinColoumns == @JoinColumn(name="p_id"),
@JoinColumn(name="p_id"),
InverseJoinColumns
InverseJoinColumns == @JoinColumn
@JoinColumn (name
(name == "a_id")
"a_id")
))
private
private Set<Adresse>
Set<Adresse> adresses;
adresses;
……
}}
64
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 49 / 173
L’annotation @JoinTable
L’annotation @JoinTable :
» donne des informations sur la table association qui va
représenter l’association
» attribut name donne le nom de la table
» attribut joinColumns donne les noms des attributs de la
table qui référencent les clés primaires du côté propriétaire
de l’association
» attribut inverseJoinColumns donne les noms des attributs
de la table qui référencent les clés primaires du côté qui
n’est pas propriétaire de l’association
» Attribut uniqueConstraints qui permet de définir les
contraintes sur la table de jointure
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 50 / 173
Collections de type simple
Il arrive souvent qu’une entité ait comme attribut une collection
de valeurs de type simple (par exemple un mémoire a un
ensemble de mots clés), JPA nous donne la possibilité de gérer
ces types de situations avec la collection de type simple.
L’annotation @ElementCollection est utilisée à cet effet.

@Entity
@Entity
public
public class
class Memoire{
Memoire{
@Id
@Id
private
private int
int id;
id;

@ElementCollection
@ElementCollection
@CollectionTable(name
@CollectionTable(name == "MOTS_CLES")
"MOTS_CLES")
private
private Set<String>
Set<String> motsCles;
motsCles;
….
….
}} fg

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 51 / 173


Collections de type simple
L’annotation @CollectionElement prend les
attributs optionnels:
» fetch(FetchType) qui spécifie si les valeurs doivent
être chargées de manière agressive ou à la
demande
» targetClass (java.lang.Class) qui spécifie le type de
base ou embeddable de la collection
L’annotation @CollectionTable permet de
redéfinir la table pour la collection et a les
mêmes attributs que @Table.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 52 / 173
Collections de type embedabble
@Embeddable
@Embeddable
public
public class
class Adresse
Adresse {{
private
private String
String rue;
rue;
private
private String
String ville;
ville;
private
private String
String cp;
cp;
...
...
}}

@Entity
@Entity public
public class
class Millionaire
Millionaire extends
extends Personne
Personne {{
……
@ElementCollection
@ElementCollection
protected
protected Set<Adresse>
Set<Adresse> maisonVacances;
maisonVacances;
.. ....
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 53 / 173


Collections de type embeddable
@Embeddable
@Embeddable
public
public class
class Adresse
Adresse {{
private
private String
String rue;
rue;
private
private String
String ville;
ville;
private
private String
String cp;
cp;
...
...
}}

@Embeddable
@Embeddable
public
public class
class InfoContact
InfoContact {{
@Embedded
@Embedded private
private Adresse
Adresse adresse;
adresse;
...
...
}}

@Entity
@Entity public
public class
class Employe
Employe {{
@Id
@Id private
private int
int empId;
empId;
private
private String
String nom;
nom;
private
private InfoContact
InfoContact contact;
contact;
...
...
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 54 / 173


Embeddable avec associations

@Embeddable
@Embeddable
public
public class
class InfoContact
InfoContact {{
@Embedded
@Embedded
private
private Adresse
Adresse adresse;
adresse;
@OneToMany
@OneToMany
private
private Set<Telephone>
Set<Telephone> telephones;
telephones;
...
...
}}

@Entity
@Entity
public
public class
class Employe
Employe {{
@Id
@Id
private
private int
int empId;
empId;
private
private String
String nom;
nom;
private
private InfoContact
InfoContact contact;
contact;
...
...
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 55 / 173


Listes triées
L’on peut avoir une collection de valeurs triées en utilisant
l’annotation @OrderColumn.
Elle a les attributs columnDefinition, insertable, updatable et
nullable et ces attributs ont la même signification que pour
@Column. Elle a en plus l’attribut name qui définit la colonne sur
laquelle se fera le tri.
@Entity
@Entity
public
public class
class CarteCredit
CarteCredit {{
@Id
@Id
private
private long
long numCarte;
numCarte;
@OneToOne
@OneToOne
private
private Personne
Personne proprioCarte;
proprioCarte;
...
...
@OneToMany
@OneToMany
@OrderColumn
@OrderColumn
private
private List<TransactionCarte>
List<TransactionCarte> transactions;
transactions;
45 }}
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 56 / 173
Les Maps
@Entity
@Entity
public
public class
class VideoStore
VideoStore {{
@Id
@Id
private
private Integer
Integer storeId;
storeId;
private
private Adresse
Adresse emplacement;
emplacement;
...
...
@ElementCollection
@ElementCollection
Map<Film,
Map<Film, Integer>
Integer> stock;
stock;
}}

@Entity
@Entity
public
public class
class Film
Film {{
@Id
@Id
private
private String
String titre;
titre;
private
private String
String metteurEnScene;
metteurEnScene;
...
...
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 57 / 173


Persistance des entités associées
Persistance des entités associées :
» lorsqu’un objet o est rendu persistant, les objets référencés
par o devraient être rendus persistants
» ‣ persistance par transitivité
» ‣ comportement logique, mais…
» complexe !
› cohérence
› par exemple, que se passe-t-il si un objet supprimé est référencé par
un autre objet ?
» JPA n’effectue pas de persistance par transitivité par défaut
› plus de souplesse
› possibilité d’avoir la persistance par transitivité avec cascade

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 58 / 173


Persistance des entités associées
Attribut cascade :
» permet de propager les effets d’une opération sur les
entités associées
» valeurs possibles:
› PERSIST, REMOVE, REFRESH, MERGE, ALL, DETACH
» @OneToMany(cascade=CascadeType.PERSIST)
Par défaut, aucune opération n’est appliquée
transitivement
En JPA 2.0, suppression automatique des orphelins
» @OneToMany(cascade=CascadeType.PERSIST,
orphanRemoval=true)
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 59 / 173
L’attribut cascade
L’attribut cascade de type CascadeType permet de définir le
comportement d’une entité associée
» DETACH : Si l’entité parente est détachée du contexte de persistance,
alors l’entité associée l’est aussi.
» MERGE : Si l’entité parente est remise dans le contexte de persistance,
alors l’entité associée l’est aussi.
» PERSIST : Si l’entité parente est sauvegarde alors l’est aussi l’entité
associée
» REFRESH : Si l’entité parente est rafraîchie dans le contexte de
persistance, alors l’est aussi l’entité associée
» REMOVE : si l’entité parente est supprimée du contexte de persistance,
alors l’entité associée l’est aussi.
» ALL : équivalent à cascade = {DETACH, MERGE, PERSIST, REFRESH,
REMOVE}
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 60 / 173
Les entités associées
Récupération des entités associées
» lorsqu’une entité est récupérée (requête), est-ce qu’on
récupère aussi les entités associées ?
» et si on récupère les entités associées, est-ce que l’on
récupère les entités associées des entités associées ?
» et si... !
JPA laisse le choix de récupérer ou non les entités
associées
» on peut choisir le mode de récupération: LAZY ou EAGER
» EAGER: immédiat ou de manière agressive
» LAZY: à la demande
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 61 / 173
Les entités associées
LAZY
» pour les associations avec des hiérarchies importantes
» dans ce cas, l’entité associée n’est pas récupérée
immédiatement
» JPA remplace l’entité par un proxy qui permettra de
récupérer l’entité plus tard si besoin
» le proxy contient la clé primaire
Par défaut
» JPA est en mode EAGER pour 1-1 et N-1
» JPA est en mode LAZY pour 1-N et M-N
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 62 / 173
Les requêtes

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 63 / 173


La génération de la base de
données
Une implémentation JPA peut être
configurée pour créer automatiquement les
tables, charger les données dans les tables
et supprimer des tables lors du déploiement
de l’application en utilisant le fichier de
description standard de l’application.
Ces tâches sont généralement réalisées lors
de la phase de développement et non de
déploiement
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 64 / 173
Génération de la base de données
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="examplePU" transaction-type="JTA">
<jta-data-source>java:global/ExampleDataSource</jta-data-source>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
<property name="javax.persistence.schema-generation.create-source" value="script"/>
<property name="javax.persistence.schema-generation.create-script-source" value="META-INF/sql/
create.sql" />
<property name="javax.persistence.sql-load-script-source" value="META-INF/sql/data.sql" />
<property name="javax.persistence.schema-generation.drop-source" value="script" />
<property name="javax.persistence.schema-generation.drop-script-source" value="META-INF/sql/
drop.sql" />
</properties>
</persistence-unit>
</persistence>

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 65 / 173


Génération de la base de données
L’on peut configurer la création et la suppression
des tables de la base de données en utilisant la
propriété javax.persistence.schema-
generation.database.action
Elle peut prendre les valeurs suivantes :
» none : aucune création ni suppression n’est effectuée
» create : Le fournisseur va créer les différentes tables
» drop-and-create : les éléments sont supprimés et puis
recréés après
» drop : les éléments de la base sont supprimés
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 66 / 173
Génération de la base de données
Par défaut, l’implémentation utilise les informations de
mapping pour générer les tables. L’on peut aussi utiliser un
script pour la création ou en suppression en utilisant les
propriétés javax.persistence.schema-generation.create-
source et javax.persistence.schema-generation.drop-source
Les valeurs possibles sont :
» metadata: l’on utilise les mappings pour créer ou supprimer
» script: l’on utilise un script pour créer ou supprimer les tables
» metadata-then-script : l’on utilise une combinaison mapping suivie
du script
» script-then-metadata : l’on utilise le script puis les informations de
mapping

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 67 / 173


Génération de la base de données
Si l’on spécifie script dans create-source ou
drop-source, l’on spécifie la localisation du
fichier en utilisant javax.persistence.schema-
generation.create-script-source ou
javax.persistence.schema-generation.drop-
script-source.
La localisation du fichier est relative à la
racine de l’unité de persistance.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 68 / 173


SIMPLIFIER L’ÉCRITURE DES DAO
AVEC SPRING DATA

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 69 / 173


Introduction
– L’objectif de Spring Data est de réduire
significativement la quantité de code
répétitif nécessaire à la mise en œuvre des
DAO.
– Spring Data offre des abstractions pour un
ensemble de sources de données
– En particulier, Spring Data supporte JPA.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 70 / 173


Les concepts de base
– L’interface central est l’interface Repository.
Elle prend la classe d’une entité ainsi que le
type de la clé primaire.
– Cette interface agit plus comme un
marqueur (marker interface) pour capturer
le type de travail et aide à découvrir les
interfaces qui l’étendent

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 71 / 173


Les concepts de base
– L’interface CrudRepository fournit des
fonctionnalités CRUD pour la classe de
l’entité qui est gérée.

public
public interface
interface CrudRepository<T,
CrudRepository<T, ID ID extends
extends Serializable>
Serializable> extends
extends Repository<T,
Repository<T, ID>
ID> {{
<S
<S extends
extends T>
T> SS save(S
save(S entity);
entity);
Optional<T>
Optional<T> findById(ID
findById(ID primaryKey);
primaryKey);
Iterable<T>
Iterable<T> findAll();
findAll();
long
long count();
count();
void
void delete(T
delete(T entity);
entity);
boolean
boolean existsById(ID
existsById(ID primaryKey);
primaryKey);
//// ...
... more
more functionality
functionality omitted.
omitted.
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 72 / 173


L’interface CrudRepository
Cette interface offre les méthodes suivantes ;
– save pour enregistrer ou mettre à jour une entité
– findById renvoie l’entité dont la clé primaire est en
entrée
– findAll renvoie toutes les entités
– count renvoie le nombre d’entité
– delete supprime de la source de données l’entité en
entrée
– existsById indique s’il existe une entité avec la clé
primaire en entrée
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 73 / 173
L’interface
PagingAndSortingRepository
Au dessus de l’intercae CrudRepository nous
avons l’interface
PagingAndSortingRepository  qui ajoute des
méthodes pour faciliter l’accès paginés aux
entités.

public
public interface
interface PagingAndSortingRepository<T,
PagingAndSortingRepository<T, ID
ID extends
extends Serializable>
Serializable>
extends
extends CrudRepository<T,
CrudRepository<T, ID>ID> {{
Iterable<T>
Iterable<T> findAll(Sort
findAll(Sort sort);
sort);
Page<T>
Page<T> findAll(Pageable
findAll(Pageable pageable);
pageable);
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 74 / 173


L’interface JPARepository
– Spring Data offre des interfaces pour
travailler directement avec certains
technologies comme JPA ou MongoDB.
– L’interface JPARepository étend l’interface
CrudRepository pour exposer les capacités
spécifiques de JPA.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 75 / 173


Définition des requêtes
Une DAO, en plus des méthodes CRUD, offre
des requêtes.
Spring Data offre deux mécanismes pour
dériver une méthode :
– Soit directement à partir du nom de la
méthode
– Soit en définissant manuellement la requête
à exécuter.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 76 / 173


Les requêtes
Spring Data offre trois stratégies pour identifier la requête
à exécuter  (la stratégie peut être définie en utilisant
l’attribut queryLookupStrategy de l’annotation
EnableJPARepositories):
– CREATE permet dériver la requête à partir du nom de la
méthode. Certains préfixes bien définis vont être retirés du
nom de la méthode et le reste est analysé convenablement.
– USE_DECLARED_QUERY essaie de chercher une requête
dont le nom est le nom de la méthode
– CREATE_IF_NOT_FOUND (par défaut) combine CREATE et
USE_DECLARED_QUERY

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 77 / 173


La création des requêtes
Le mécanisme de construction des requêtes est utile pour
construire des requêtes contraignante sur les entités.
Le mécanisme supprime les préfixes find...By, read...By,
query...By, count...By et get...By du nom de la méthode et
analyse le reste.
La clause initiale peut aussi contenir Distinct.
Le premier By est utilisé comme délimiteur initial et
indique le début de la requête.
Au niveau élémentaire, l’on définit les conditions sur les
propriétés de l’entité qui peut être combinées grâce à And
et Or
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 78 / 173
La création des requêtes
Les règles de base pour la création des requêtes sont :
– Les expressions permet généralement de traverser des
propriétés et peuvent être combinées grâce à AND et
OR. Nous pouvons aussi avoir des opérateurs tels
Between, LessThan, GreaterThan, Like pour les
propriétés.
– L’analyse de la méthode supporte la clause IgnoreCase
ou bien all pour toutes les propriétés.
– L’on peut aussi ajouter à la fin OrderBy pour fournir un
mécanisme de tri en indiquant la direction (Asc ou
Desc).
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 79 / 173
Les expressions de la propriété
– L’expression d’une propriété fait référence à
une propriété directe de l’entité gérée.
– Il est tout à fait possible de parcourir les
propriétés. Si la classe Person a un attribut
address qui a aussi un attribut zipCode alors
la méthode
List<Person> findByAddressZipCode(ZipCode zipCode);

crée le parcours x.address.zipCode.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 80 / 173


Expressions de la propriétés
L’algorithme de résolution commence par
examiner le texte en entier (AddressZipCode).
Si la propriété n’existe pas alors il va considérer
AddressZip et le cas echéant Address.
Cet algorithme marche pour la plupart des cas,
mais il peut arriver des situations où il y’a
ambiguïté. Par exemple, l’entité a la propriété
address et addressZip. Dans ce cas l’on peut
utiliser le caractère underscore ( _ ) comme
délimiteur.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 81 / 173
Les requêtes
Spring Data reconnaît certains paramètres
spécifiques comme Pageable et Sort pour
appliquer la pagination et le tri
dynamiquement.
L’on peut aussi limiter les résultats grâce aux
mots clés first ou top. Une valeur numérique
optionnelle peut être utiliser pour spécifier
le nombre maximum d’entités retournées.
Par défaut, la valeur 1 est considérée.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 82 / 173
Les mots clés supportés
Mots
Mots clés
clés Exemple
Exemple JPQL
JPQL
And
And findByLastnameAndFirstname
findByLastnameAndFirstname …
… where
where x.lastname
x.lastname =?1
=?1 and
and
x.firstname
x.firstname =?2
=?2
Or
Or findByLastnameOrFirstname
findByLastnameOrFirstname …
… where
where x.lastname
x.lastname =?1
=?1 or
or
x.firstname =?2
x.firstname =?2
Is,
Is, Equals
Equals FindByFirstname,
FindByFirstname, findByFirstnameIs,
findByFirstnameIs, …
… where
where x.firstnmae
x.firstnmae =?1
=?1
findByFirstnameEquals
findByFirstnameEquals
Between
Between findByStartDateBetween
findByStartDateBetween …
… where
where x.startDate
x.startDate between?1
between?1
and
and ?2?2
LessThan
LessThan findByAgeLessThan
findByAgeLessThan …
… where
where x.age
x.age << ?1
?1
LessThanEquals
LessThanEquals findByAgeLessThanEquals
findByAgeLessThanEquals …
… where
where x.age
x.age ≤≤ ?1
?1
GreaterThan
GreaterThan findByAgeGreaterThan
findByAgeGreaterThan …
… where
where x.age
x.age >> ?1
?1
GreaterThanEquals
GreaterThanEquals findByAgeGreaterThanEquals
findByAgeGreaterThanEquals …
… where
where x.age
x.age >=
>= ?1
?1
After
After findByStartDateAfter
findByStartDateAfter …
… where
where x.startDate
x.startDate >?1
>?1
Before
Before findByStartDateBefore
findByStartDateBefore …
… where
where x.startDate
x.startDate <?1<?1
isNull
isNull findByAgeIsNull
findByAgeIsNull …
… where
where x.age
x.age is
is null
null

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 83 / 173


Les mots clés supportés
Mots
Mots clés
clés Exemple
Exemple JPQL
JPQL
IsNotNull,
IsNotNull, findByAge(Is)NotNull
findByAge(Is)NotNull …
… where
where x.age
x.age not
not null
null
NotNull
NotNull
Like
Like findByFirstnameLike
findByFirstnameLike …
… where
where x.firstname
x.firstname like?1
like?1
NotLike
NotLike findByFirstnameNotLike
findByFirstnameNotLike …
… where
where x.firstname
x.firstname not
not like?1
like?1
StartingWith
StartingWith findByFirstnameStartingWith
findByFirstnameStartingWith …
… where
where x.firstname
x.firstname like
like ?1
?1
EndingWith
EndingWith findByFirstnameEndingWith
findByFirstnameEndingWith …
… where
where x.firstname
x.firstname like?1
like?1
Containing
Containing findByFirstnameContaining
findByFirstnameContaining …
… where
where x.firstname
x.firstname like?1
like?1
OrderBy
OrderBy findByAgeOrderByLastnameDesc
findByAgeOrderByLastnameDesc …
… where
where x.age
x.age =?1
=?1 order
order by
by
x.lastname desc
x.lastname desc
Not
Not findByLastnameNot
findByLastnameNot …
… where
where x.lastname
x.lastname <>?1
<>?1
In
In findByAgeIn(Collection<Age>
findByAgeIn(Collection<Age> ages)
ages) …
… where
where x.age
x.age in?1
in?1
NotIn
NotIn findByAgeNotIn(Collection<Age>
findByAgeNotIn(Collection<Age> ages)
ages) …
… where
where x.age
x.age not
not in?1
in?1
True
True findByActiveTrue()
findByActiveTrue() …
… where
where x.active
x.active == true
true
False
False findByActiveFalse()
findByActiveFalse() …
… where
where x.active
x.active == false
false
IgnoreCase
IgnoreCase findByFirstnameIgnoreCase
findByFirstnameIgnoreCase …
… where
where UPPER(x.firstname)
UPPER(x.firstname) ==
UPPER(?1)
UPPER(?1)

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 84 / 173


Utilisation des NamedQueries
– Il est possible de déclarer la requête à
exécuter grâce à l’annotation NamedQuery.
– SpringData va faire la correspondance entre
le nom de la requête et le nom de la
méthode.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 85 / 173


Utilisation des NamedQueries

@Entity
@Entity
@NamedQuery(name
@NamedQuery(name == "User.findByEmailAddress",
"User.findByEmailAddress",
query
query == "select
"select uu from
from User
User uu where
where u.emailAddress
u.emailAddress == ?1")
?1")
public
public class
class User
User

}}

public
public interface
interface UserRepository
UserRepository extends
extends JpaRepository<User,
JpaRepository<User, Long>
Long> {{
List<User>
List<User> findByLastname(String
findByLastname(String lastname);
lastname);
User
User findByEmailAddress(String
findByEmailAddress(String emailAddress);
emailAddress);
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 86 / 173


Utilisation de Query
– L’utilisation des NamedQueries pour
déclarer les requêtes marche bien pour un
petit nombre de requêtes.
– Il est possible de directement déclarer la
requête à exécuter lorsque l’on déclare la
méthode grâce à l’annotation @Query
– L’annotation Query à une précédence que
les NamedQueries.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 87 / 173


Utilisation de Query

public
public interface
interface UserRepository
UserRepository extends
extends JpaRepository<User,
JpaRepository<User, Long>
Long> {{
@Query("select
@Query("select uu from
from User
User uu where
where u.emailAddress
u.emailAddress == ?1")
?1")
User
User findByEmailAddress(String
findByEmailAddress(String emailAddress);
emailAddress);
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 88 / 173


Utilisation de Query
– Par défaut les arguments suivent l’ordre de
leur définition dans la requête mais il est
possible de le changer grâce à l’annotation
@Param.

public
public interface
interface UserRepository
UserRepository extends
extends JpaRepository<User,
JpaRepository<User, Long> Long> {{
@Query("select
@Query("select uu from
from User
User uu where
where u.firstname
u.firstname == :firstname
:firstname oror u.lastname
u.lastname ==
:lastname")
:lastname")
User
User findByLastnameOrFirstname(@Param("lastname")
findByLastnameOrFirstname(@Param("lastname") String String lastname,
lastname,

@Param("firstname")
@Param("firstname") String
String firstname);
firstname);
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 89 / 173


EXPOSER LES SERVICES SOUS
FORME DE REST WEBSERVICES

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 90 / 173


Introduction
– Les webservices sont l’une des méthodes les
plus populaires pour la mise en œuvre des
applications client/serveur.
– Ils sont utilisés pour faire communiquer un
client et un serveur à travers Internet.
– Les webservices sont, de ce fait, les
composants des applications web qui
peuvent être publiés, recherchés et utilisés
à travers Internet.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 91 / 173
Introduction
– Un webservice a une interface qui décrit les
services fournis connue sous comme WSDL
(Web Services Description Language).
– La communication entre le client et le
serveur se fait au travers du protocole HTTP
en utilisant le XML et d’autres technologies
liées au web.
– Les webservices rendent le travail
d’intégration plus facile
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 92 / 173
Introduction
Les webservices sont utilisés
traditionnellement pour deux raisons :
– Beaucoup de compagnies qui offrent des
services internet ont ouverts leurs
infrastructures au public en utilisant des
APIs bien définies.
– Les web services sont utilisés pour
interconnecter des applications
indépendantes entre elles.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 93 / 173
Introduction
Il existe deux approches pour la mise en
œuvre des web services :
– SOAP (Simple Object Access Protocol) qui
est un standard pour décrire les messages
échangés (requêtes et réponses). Les
messages sont des documents XML
– REST (Representational State Transfer) qui
est un style d’architecture décrit par Roy
Fielding dans sa thèse.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 94 / 173
REST web services
REST ne décrit pas une architecture mais est un ensemble de contraintes pour
créer un style d’architecture qui peut être utilisé pour la mise en œuvre des
applications distribuées.
Les contraintes suivantes sont définies :
– Client / Serveur : Il doit avoir un couplage fait entre le serveur et le client.
– Sans état : Il n’est pas nécessaire de maintenir des sessions utilisateurs entre les
requêtes.
– Mise en cache : le service doit supporter la mise en cache des données
(échangées)
– Une interface uniforme : Une interface générique qui permet une interaction
entre le client et le serveur de manière unifiée. Chaque ressource exposée doit
avoir une adresse unique et est accessible à travers l’interface générique (un
ensemble de méthodes standards).
– Un système en couche : Le serveur peut avoir avoir plusieurs couches pour
faciliter la scalabilité.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 95 / 173
Le protocole HTTP

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 96 / 173


Introduction
HyperText Transfer Protocol (HTTP) définit la fondation
de la communication sur le web.
HTTP est un protocole qui définit comment les
messages hypertextes sont formatés, transmises et
traitées sur Internet.
Il existe quatre versions majeures du protocole :
– HTTP/0.9 publiée en 1991
– HTTP/1.0 publiée en 1996
– HTTP/1.1 le plus populaire et publiée en 1999
– HTTP /2.0 la version récente publiée en 2015
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 97 / 173
URIs et URLs
Chaque ressource disponible au niveau du
serveur a un nom.
Le nom de la ressource est une URI (Uniform
Resource Identifier). C'est un peut l'équivalent
d'une adresse postale utilisée sur Internet.
La forme la plus courante d'URI est une URL
(Uniform Resource Locator).
Une URL décrit précisément la location d'une
ressource sur un serveur donnée.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 98 / 173
URIs et URLs
Une URL a plusieurs parties :
– Le protocole utilisée
– L’hôte qui désigne l'adresse de la machine.
– Le chemin vers la ressource
– Le port utilisé pour communiquer avec le serveur
– Une requête une ensemble de pair nom=valeur séparé par
&.
– Un fragment.
La forme générale d'une URL est la suivante :
Protocole://hote:port/chemin?requete#fragment.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 99 / 173
URIs et URLs

http://www.douwe.com:80/cours?code=ITEL242#web
http://www.douwe.com:80/cours?code=ITEL242#web

protocole
protocole hôte
hôte port
port chemin requête
chemin requête fragment
fragment

ftp://173.168.2.5/dossier/archiver.tar.gz
ftp://173.168.2.5/dossier/archiver.tar.gz

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 100 / 173


Les transactions HTTP
Les clients et les serveurs web réalisent des
transactions HTTP.
Une transaction HTTP est constituée d'une
requête (du client vers le serveur) et d'une
réponse (du serveur vers le client).
Au cours de cette transaction sont échangés
des messages HTTP.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 101 / 173


Les messages HTTP
Les messages HTTP constitués de simples séquences
de lignes de caractères.
Ils sont faciles à lire.
Les messages HTTP sont constitués de trois parties :
– Un début de ligne qui indique ce que l'on doit faire.
– Les champs entête. 0 ou plusieurs champs entête
suivent le début de ligne. Il s'agit d'un ensemble
nom valeur séparés par :
– Le corps du message après une ligne vide.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 102 / 173
Les messages HTTP
Les messages envoyés du client vers le serveur
sont appelés les requêtes HTTP.
Les messages envoyés du serveur vers le client
sont appelés les réponses HTTP.
Le corps du message est une partie
optionnelle et il est courant que les requête
HTTP ne comportent pas de corps.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 103 / 173


Les requêtes HTTP
Le début de ligne d'une requête dit au serveur de
faire quelque chose.
Le format d'une requête HTTP est de la forme
suivante :
<méthode> <chemin ressource> <version protocole>
<champs entête>
<ligne vide>
<corps message>
Les fragments ne sont jamais envoyés au serveur. Ils sont
utilisés par le client.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 104 / 173
Les requêtes HTTP
GET /cours?code=ITEL242 HTTP/1.1
Accept : text/*
Host: www.douwe.com

Ici la méthode utilisée est GET et la ressource


demandée est à l'adresse /cours?
code=ITEL242 et l'on utilise la version 1.1 du
protocole HTTP.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 105 / 173
Les méthodes HTTP
Plusieurs méthodes peuvent être utilisées avec une requête HTTP :
La méthode GET : Elle est utilisée pour récupérer les ressources sur
le serveur
La méthode HEAD : Elle est similaire à GET à la seule différence
que seule le début de ligne et les champs d’entête sont transférés
(le corps n’est pas transféré)
La méthode POST : Elle est utilisée pour poster (envoyer) les
données sur le serveur. La donnée sera stockée à l’URI fournie
La méthode PUT : elle est utilisée pour mettre à jour la donnée
pointée par l’URI fournie.
La méthode DELETE : elle est utilisée pour supprimer la donnée
pointée par l’URI.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 106 / 173


Les méthodes HTTP
En plus des cinq méthodes précédentes, d’autres
méthodes existent :
– La méthode TRACE. Elle est utilisée pour afficher le
contenu de la requête envoyée. Elle est utile pour le
débogage.
– La méthode OPTIONS. Elle est utilisée pour renvoyer les
méthodes supportées par le serveur pour une URI donnée
– La méthode CONNECT. Elle est utilisée pour établir une
connexion au serveur au travers de HTTP
– La méthode PATCH. Elle est utilisée pour appliquer un
changement partiel à la ressource identifiée par l’URI.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 107 / 173
Les réponses HTTP
Le début de ligne d'une réponse donne des
informations sue le traitement de la requête.
Une réponse HTTP a la forme suivante :
<version protocole> <code statut> <raison>
<entêtes>
<ligne vide>
<corps>
<raison>fournit une description textuelle de ce qui
s'est passé lorsque l'on traitait la requête.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 108 / 173
Les réponses HTTP
L'exemple suivant montre une réponse HTTP.
HTTP/1.0 200 OK
Content-type: text/plain
Content-length: 256

<html>
<head><title>Toto</title></head>
<body>
<h1>Je suis là toto</h1>
</body>
</html>
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 109 / 173
Les codes de statut
Dans une réponse HTTP, il y’a un code de réponse. Le HTTP
distingue 5 grandes classes de codes de statut :
– 1xx : Ces séries de codes donnent des informations sur le contenu
de la réponse.
– 2xx : Ces séries de codes indiquent que la requête s’est
correctement exécutée.
– 3xx : Ces séries de codes indiquent une redirection c’est à dire
que le client a besoin d’effectuer des actions supplémentaires
pour terminer l’exécution de la requête.
– 4xx : Ces séries de codes indiquent une erreur au niveau du client.
– 5xx : Ces séries de codes indiquent une erreur au niveau du
serveur.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 110 / 173


Les codes de statut
Les codes fréquemment utilisés sont :
– 100 Continue : indique que le serveur à reçu l’entête de la requête et que le
client peut envoyer le corps de la requête.
– 101 switching Protocol : indique que le serveur est d’accord pour une
changement de protocole
– 102 Processing : indique au client d’attendre d’autres parties de la réponse
– 200 OK : indique que la requête s’est bien exécutée et la réponse renvoyée
– 201 Created : indique que la requête s’est bien exécutée et qu’une
ressource est créée.
– 204 No Content : indique que la requête s’est bien exécutée mais qu’il n’y a
pas de valeur retournée
– 304 Not Modified : indique que la ressource n’a pas été modifiée depuis le
dernier accès.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 111 / 173


Les codes de statut
– 400 Bad Request : indique que le serveur n’a pas pu exécuter la requête à cause d’une
syntaxe incorrecte dans la requête
– 401 Unauthorized : indique qu’une authentification est requise pour accéder à la
ressource.
– 403 Forbidden : indique que le serveur refuse de répondre à la requête même si la
requête est valide. La raison est dans le corps de la réponse s’il ne s’agit pas d’une
requête HEAD
– 404 Not Found : indique que la ressource spécifiée à l’adresse fournie n’existe pas.
– 405 Method Not Allowed : indique que la méthode utilisée n’est pas supportée pour
la ressource.
– 408 Request Timeout : indique que le client n’a pas pu répondre dans le temps imparti
par le serveur
– 409 Conflict : indique que la requête ne peut être exécutée à cause d’un conflit ou
d’une règle de validation
– 500 Internal Server Error : il s’agit d’un code générique qui indique qu’une erreur s’est
produite au niveau du serveur.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 112 / 173


Les champs d’entête
Un champ entête ajoute une information
supplémentaire à la requête ou à la réponse.
Ils sont principalement une liste de
nom/valeur séparée par :
La spécification HTTP définit plusieurs champs
entête.
Les applications sont aussi libres de définir
leurs propres champs entête.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 113 / 173


Les champs d’entête
Les champs entête sont classés suivant les catégories suivantes :
– Les entêtes générales. Elles peuvent apparaître aussi bien
dans les requêtes que les réponses.
– Les entêtes requête. Elles fournissent des informations
concernant les requêtes.
– Les entêtes réponse. Elles fournissent des informations
concernant les réponses.
– Les entêtes entité. Elles décrivent la taille et les contenus du
corps.
– Les extensions entête. Les entêtes non définies par la
spécification.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 114 / 173


Les champs d’entête
Il existe plusieurs champs d’entête standardisés. Dans
notre contexte le plus important est Content-Type qui
décrit le type du contenu du corps du message.
Le champ d’entête Accept dit au serveur quel type de
réponse le client attend.
Les valeurs les plus utilisées pour Content-Type sont :
text (text/html), multipart (multipart/form-data),
message (message/partial), image (image/png), audio
(audio/mpeg), video (video/mp4), application
(application/json)

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 115 / 173


LES FONDAMENTAUX D’UNE API
RESTful

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 116 / 173


Définition
Une API Web RESTful ou API RESTful est une
API implémentée en utilisant HTTP comme
protocole et respectant les contraintes
d’une architecture REST.
Une architecture style REST est définie en
spécifiant les éléments clés de l’architecture
(Données, Composantes et Connecteurs)
ainsi qu’un ensemble de contraintes.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 117 / 173


Définition

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 118 / 173


Les données
Une propriété centrale qui permet de distinguer REST
des autres styles basés sur les réseaux est son emphase
sur une interface uniforme entre les composants.
Cette interface générique fait abstraction des
informations transférées.
Cette interface unifiée permet de gérer toutes les
interactions entre le client et le serveur d’une façon
unifiée.
Toutes les ressources qui interviennent dans la
communication entre le serveur et le client utilisent un
ensemble fixe d’opérations.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 119 / 173
Les ressources
Une ressource dans le contexte d’une API
RESTful est tout objet adressable à partir du
web.
Une ressource est ainsi une correspondance
logique et temporelle entre un concept du
domaine d’application pour lequel nous
sommes entrain d’implémenter une
solution.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 120 / 173


Représentation des ressources
L’idée de base de l’interface générique est d’associer des actions aux
différentes méthodes HTTP.
L’on peut ainsi faire une correspondance aisée avec les opérations
CRUD. En d’autres termes, utiliser les URIs pour connecter le serveur
et le client pour échanger les données
Action
Action Equivalent
Equivalent HTTP
HTTP

CREATE
CREATE POST
POST ou
ou PUT
PUT

READ
READ GET
GET

UPDATE
UPDATE PUT
PUT ou
ou PATCH
PATCH

DELETE
DELETE DELETE
DELETE
Cd
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 121 / 173
Représentation des ressources
Si l’on dispose d’une ressource Etudiant,
nous avons la correspondance suivante :
Méthode
Méthode URL
URL Action
Action
GET
GET /etudiants
/etudiants Renvoie
Renvoie tous
tous les
les étudiants
étudiants

GET
GET /etudiants/2
/etudiants/2 Renvoie
Renvoie l’étudiant
l’étudiant dont
dont l’id
l’id vaut
vaut 22

POST
POST /etudiants/
/etudiants/ Crée
Crée un
un nouvel
nouvel étudiant
étudiant

PUT
PUT /etudiants/2
/etudiants/2 Met
Met àà jour
jour l’étudiant
l’étudiant dont
dont l’id
l’id est
est 22

DELETE
DELETE /etudiants/2
/etudiants/2 Supprime
Supprime l’étudiant
l’étudiant dont
dont l’id
l’id vaut
vaut 22

DELETE
DELETE /etudiants/
/etudiants/ Supprime
Supprime tous
tous les
les étudiants
étudiants

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 122 / 173


MISE EN OEUVRE DES RESTFUL
WEBSERVICES AVEC JAX-RS

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 123 / 173


Introduction
JAX-RS (Java Api for RESTful web services) fait
partie intégrale de JAVA EE qui définit des
standards pour développement des applications
java portables, robustes, extensibles et sécurisées.
JAX-RS fut introduit dans la version 6 de JAVA EE
basé sur la JSR 311.
JAX-RS 2.0 (basé sur JSR 339) faisait partie de JAVA
EE 7.
JAX-RS 2.1 (basé sur JSR 370) fait partie de JAVA
EE 8.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 124 / 173
Introduction
JAX-RS est une spécification (tout comme JPA) et
pour travailler avec, il faut une implémentation.
Il existe plusieurs implémentations de JAX-RS :
– Jersey : C’est l’implémentation de référence de JAX-
RS
– Apache CXF : Implémentation de la fondation Apache.
Elle supporte JAX-WS et JAX-RS
– RESTEasy : implémentation de JBoss.
– Restlet : implémentation légère pour les RESTFul web
services.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 125 / 173
Introduction
L’objectif de JAX-RS est de rendre le développement des
RESTFul web services en java facile et portable.
Pour commencer, il faut ajouter la dépendance vers l’API
JAX-RS. Si l’on utilise Maven, alors il faut ajouter la
dépendance suivante dans le fichier pom.xml
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1</version> <!-- mettre la bonne version -->
<scope>provided</scope><!-- si serveur d’application -->
</dependency>

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 126 / 173


Les annotations JAX-RS

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 127 / 173


Les ressources RESTful
Les ressources REST sont les éléments
fondamentaux des services RESTful.
Elle esr d’un type spécifique et expose un
ensemble d’opérations correspondant aux
méthodes HTTP.
Une classe ressource est un POJO (Plain Old
Java Object).
Chaque classe ressource doit avoir au moins
une méthode annotée avec @javax.ws.rs.Path
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 128 / 173
L’annotation Path
l’annotation Path indique l’URI
correspondant à la classe de ressource ou la
méthode HTTP à laquelle elle doit répondre.
L’URI indiquée est relative à l’URI du serveur
sur lequel s’exécute le web service.
Cette annotation peut être appliquée à une
classe ou à une méthode.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 129 / 173


L’annotation Path
import
import javax.ws.rs.Path ;
javax.ws.rs.Path ;

@Path("etudiants")
@Path("etudiants")
public
public class
class EtudiantRessource{
EtudiantRessource{
//// Le
Le reste
reste du
du code
code est
est ici
ici
}}

Ce fragment de code définit une URI relative


/etudiants. L'URI complète sera donc :
http://hote:port/context-root/application-
path

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 130 / 173


L’annotation Path
import
import javax.ws.rs.GET ;
javax.ws.rs.GET ;
import javax.ws.rs.Path ;
import javax.ws.rs.Path ;
import
import javax.ws.rs.Produces ;
javax.ws.rs.Produces ;

@Path("etudiants")
@Path("etudiants")
public
public class
class EtudiantRessource{
EtudiantRessource{
@GET
@GET
@Path("count")
@Path("count")
@Produces("text/plain")
@Produces("text/plain")
public
public Integer
Integer getTotalEtudiants(){
getTotalEtudiants(){
return countAllEtudiants() ;
return countAllEtudiants() ;
}}
}}

L’URI de la méthode getTotalEtudiants sera


donc /etudiants/count et elle ne sera
invoquée que si la méthode utilisée est GET.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 131 / 173


L’annotation Path
Très souvent le client passe un certain nombre
d’informations que le serveur doit extraire. Par exemple si
l’on veut supprimer l’étudiant dont l’id est 10 le client va
émettre une requête Delete à l’URI /etudiants/10.
import
import javax.ws.rs.Path ;
javax.ws.rs.Path ;
import javax.ws.rs.DELETE ;
import javax.ws.rs.DELETE ;

@Path("etudiants")
@Path("etudiants")
public
public class
class EtudiantRessource{
EtudiantRessource{
@DELETE
@DELETE @Path("{id}")
@Path("{id}")
public
public void
void removeEtudiant(@PathParam("id")
removeEtudiant(@PathParam("id") short
short id){
id){
removeEtudiant(id) ;
removeEtudiant(id) ;
}}
}}

L’annotation PathParam est utilisée pour copier la valeur de


la variable de l’URI vers le paramètre de la méthode et la
conversion de type se fait automatiquement.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 132 / 173


L’annotation Path
JAX-RS nous donne la possibilité d’utiliser les expressions
régulières pour de restreindre les valeurs des paramètres
dans les chemins. Par exemple, l’on peut décider de
supprimer un étudiant connaissant son matricule et le
matricule à ENSPM respecte un ensemble de règles (deux
chiffres, une lettre majuscule, trois ou quatre chiffres puis la
lettre P)
@DELETE
@DELETE
@Path(
@Path(""{name:[0-9]{2}[A-Z][0-9]{3,4}P
{name:[0-9]{2}[A-Z][0-9]{3,4}P""}}
public
public void
void removeEtudiantByMatricule(@PathParam(
removeEtudiantByMatricule(@PathParam(""name
name"")) String
String matricule){
matricule){
//// l’implémentation
l’implémentation ici
ici
}}

Si la variable ne correspond pas à l’expression régulière alors


une réponse avec le code 404 Not Found est renvoyée
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 133 / 173
Spécifier les types des messages
HTTP
Le champ d’entête Content-Type permet de
spécifier le type du message HTTP.
Un web service RESTful utilise ce champ pour
indiquer le type du contenu des requêtes ou
réponses HTTP.
Avec JAX-RS, l’on utilise les annotations
@javax.ws.rs.Produces et
@javax.ws.rs.Consumes pour indiquer
respectivement le type réponses et requêtes.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 134 / 173
L’annotation @Produces
L’annotation @javax.ws.rs.Produces est utilisée pour définir le type de
média que la ressource peut renvoyer au client.
Les valeurs possibles sont :
– application/atom+xml
– application/json
– application/octet-stream
– application/svg+xml
– application/xhtml+xml
– application/xml
– text/html
– text/plain
– text/xml
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 135 / 173
L’annotation @Consumes
L’annotation @javax.ws.rs.Consumes définit le type de média que la ressource peut
accepter. L’on peut définir cette annotation que niveau de la classe (ce sera la valeur par
défaut pour toutes les méthodes) ou au niveau des méthodes.
Les valeurs possibles sont :
– application/atom+xml
– application/json
– application/octet-stream
– application/svg+xml
– application/xhtml+xml
– application/xml
– text/plain
– text/html
– text/xml
– multipart/form-data
– application/x-www-form-urlencoded

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 136 / 173


Annotations pour traiter les
requêtes
En général, un RESTful web service communication au travers
du HTTP en utilisant les verbes standards du HTTP.
JAX-RS donne la possibilité à une classe POJO de traiter les
requêtes standard du HTTP.
Nous avons les annotations suivantes :
– @javax.ws.rs.GET
– @javax.ws.rs.PUT
– @javax.ws.rs.POST
– @javax.ws.rs.DELETE
– @javax.ws.rs.HEAD
– @javax.ws.rs.OPTIONS
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 137 / 173
Accéder aux paramètres des
requêtes
JAX-RS offre des mécanismes pour extraire
certaines informations des requêtes.
L’on peut utiliser ce mécanisme pour extraire
les données d’une requête en particulier de la
requête elle même, de l’URI, du formulaire, des
cookies, des champs d’entête et de la matrice.
Ces paramètres sont la plupart du temps
utilisés en conjonction de GET, POST, PUT et
DELETE.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 138 / 173


L’annotation PathParam
Une URI, en général, pointe sur une
ressource. Il peut aussi contenir des
variables qui peuvent être utilisées pour
passer les paramètres à l’API REST.
L’annotation @javax.ws.rs.PathParam est
utilisée pour injecter la valeur du paramètre
correspondant à l’URI dans l’attribut d’un
champ, la propriété d’une ressource ou
l’argument d’une méthode.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 139 / 173
L’annotation PathParam
L’on peut aussi avoir plusieurs paramètres.
Par exemple, avoir la liste des départements
par pays et ville le chemin va ressembler à :
/departments/{country}/{city}

@Produces(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("{country}/{city}
@Path("{country}/{city} ")")
public
public List<Department>
List<Department> findAllDepartments(
findAllDepartments( @PathParam("country")
@PathParam("country") String
String countyCode,
countyCode,
@PathParam("city")
@PathParam("city") String
String cityCode)
cityCode) {{
//Find
//Find all
all departments
departments from
from the
the data
data store
store for
for aa country
country and
and city
city
List<Department>
List<Department> departments = findAllMatchingDepartmentEntities(countyCode, cityCode
departments = findAllMatchingDepartmentEntities(countyCode, cityCode ););
return
return departments;
departments;
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 140 / 173


L’annotation QueryParam
Cette annotation est utilisée pour injecter les valeurs
contenues dans la partie requête dans les paramètres
d’un champ, d’une ressource ou le paramètre d’une
méthode.
Si l’on a une URI de la forme /etudiants?
matricule=14A302S
@GET
@GET
@Produces(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public
public List<Etudiant>
List<Etudiant>
findByMatricule(@QueryParam("matricule")
findByMatricule(@QueryParam("matricule") String
String matricule)
matricule) {{
List<Etudiant>
List<Etudiant> ets=
ets= findAllMatchingEtudiant(matricule);
findAllMatchingEtudiant(matricule);
return
return ets;
ets;
}}
fds
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 141 / 173
L’annotation MatrixParam
Les paramètres de matrice sont une autre
façon de définir les paramètres dans une
URI.
Les paramètres matrices sont de la forme
nom-valeur séparés par ;
L’annotation @javax.ws.rs.MatrixParam est
utilisée pour injecter les paramètres
matrices dans les champs d’une classe, d’une
ressource ou le paramètre d’une méthode.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 142 / 173
L’annotation MatrixParam
Si l’on considère l’URI
/departments/matrix;name=IT;city=Bangalo
re :

@GET
@GET
@Produces(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("matrix")
@Path("matrix")
public
public List<Department>
List<Department> findAllDepartmentsByNameWithMatrix(
findAllDepartmentsByNameWithMatrix(
@MatrixParam("name")
@MatrixParam("name") String
String deptName,
deptName,
@MatrixParam("city")
@MatrixParam("city") String
String locationCode)
locationCode) {{
List<Department>
List<Department> depts=findAllDepartmentsFromDB(deptName,
depts=findAllDepartmentsFromDB(deptName, city);
city);
return
return depts;
depts;
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 143 / 173


L’annotation HeaderParam
Les champs d’entête fournissent des informations nécessaires
concernant le contenu des requêtes et réponses HTTP.
L’annotation @javax.ws.rs.HeaderParam pour injecter les
valeurs des champs d’entête présent dans les requêtes HTTP
dans les champs des classes, des ressources ou les paramètres
des méthodes.
L’on peut extraire le champ referrer en utilisant :

@POST
@POST
public
public void
void createDepartment(@HeaderParam("Referer")
createDepartment(@HeaderParam("Referer")
String
String referer,
referer, Department
Department entity)
entity) {{
logSource(referer);
logSource(referer);
createDepartmentInDB(department);
createDepartmentInDB(department);
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 144 / 173


L’annotation CookieParam
Cette annotation est utilisée pour injecter les paramètres des
cookies présents dans les entêtes HTTP dans le champ d’une
classe, d’une ressource ou le paramètre d’une méthode.
Le code suivant permet d’injecter la valeur Default-Dept du
cookie présent dans la requête.

@GET
@GET
@Path("cook")
@Path("cook")
@Produces(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public
public Department
Department getDefaultDepartment(@CookieParam("Default-Dept")
getDefaultDepartment(@CookieParam("Default-Dept")
short
short departmentId)
departmentId) {{
Department
Department dept=findDepartmentById(departmentId);
dept=findDepartmentById(departmentId);
return
return dept;
dept;
sqdfdecs
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 145 / 173


L’annotation FormParam
L’annotation @javax.ws.rs.FormParam est
utilisée pour injecter les paramètres des
formulaires dans les champs d’une classe,
d’une ressource ou le paramètre d’une
méthode.
Les éléments du formulaire doivent être
encodés en utilisant application/x-www-
form-urlencoded

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 146 / 173


L’annotation FormParam
<!DOCTYPE
<!DOCTYPE html>
html>
<html>
<html>
<head>
<head>
<title>Create
<title>Create Department</title>
Department</title>
</head>
</head>
<body>
<body>
<form
<form method="POST"
method="POST" action="/resources/departments">
action="/resources/departments">
Department Id:
Department Id:
<input
<input type="text"
type="text" name="departmentId">
name="departmentId">
<br>
<br>
Department
Department Name:
Name:
<input
<input type="text" name="departmentName">
type="text" name="departmentName">
<br>
<br>
<input
<input type="submit"
type="submit" value="Add
value="Add Department"
Department" />
/>
</form>
</form>
</body>
</body>
</html>
</html>

@POST
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public
public void
void createDepartment(@FormParam("departmentId")
createDepartment(@FormParam("departmentId") short
short
DepartmentId,
DepartmentId, @FormParam("departmentName")
@FormParam("departmentName") String
String departmentName)
departmentName) {{
createDepartmentEntity(departmentId,
createDepartmentEntity(departmentId, departmentName);
departmentName);
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 147 / 173


L’annotation DefaultValue
L’ annotation @javax.ws.rs.DefaultValue spécifie la
valeur par défaut pour les paramètres des requêtes en
utilisant l’une des annotation PathParam, QueryParam,
MatrixParam, CookieParam, FormParam ou HeaderParam.
La valeur par défaut est utilisée si aucune valeur n’est
trouvée.

@GET
@GET
@Produces(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public
public List<Department>
List<Department> findAllDepartmentsInRange
findAllDepartmentsInRange
(@DefaultValue("0")
(@DefaultValue("0") @QueryParam("from")
@QueryParam("from") Integer
Integer from,
from,
@DefaultValue("100")
@DefaultValue("100") @QueryParam("to")
@QueryParam("to") Integer
Integer to)
to) {{
findAllDepartmentEntitiesInRange(from,
findAllDepartmentEntitiesInRange(from, to);
to);
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 148 / 173


L’annotation Context
Le runtime JAX-RS offre différents objets du contexte qui peuvent
être utilisés pour accéder aux informations associées à la classe de la
ressource, l’environnement d’exécution etc.
L’annotation Context est utilisée pour injecter ces éléments dans les
attributs d’une classe ou le paramètre d’une méthode.

@GET
@GET
@Produces(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public
public List<Department>
List<Department> findAllDepartmentsByName(
findAllDepartmentsByName(
@Context
@Context UriInfo
UriInfo uriInfo){
uriInfo){
String
String deptName
deptName == uriInfo.getPathParameters().getFirst("name");
uriInfo.getPathParameters().getFirst("name");
List<Department>
List<Department> deptsdepts == findAllMatchingDepartmentEntities(deptName);
findAllMatchingDepartmentEntities(deptName);
return
return depts;
depts;
}}

sd
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 149 / 173
L’annotation Context
Voici la liste des classes et interfaces qui sont généralement utilisées avec
l’annotation Context :
– javax.ws.rs.core.Application : cette classe définit les composants d’une
application JAX-RS et fournit les méta data supplémentaires
– javax.ws.rs.core.UriInfo : cette interface fournit les informations sur l’application
et l’URI de la requête.
– javax.ws.rs.core.Request : cette interface fournit des méthodes pour le
traitement de la requête telles lire le type de la méthode et les préconditions
– javax.ws.rs.core.HttpHeaders : cette interface forunit l’accès aux informations
contenues dans l’entête HTTP
– javax.ws.rs.core.SecurityContext : cette interface fournit l’accès aux informations
liées à la sécurité.
– javax.ws.rs.ext.Providers : cette interface donne la possibilité de rechercher à
l’exécution une instance de l’interface provider tel MessageBodyReader,
MessageBodyWriter, ExceptionMapper et ContextResolver

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 150 / 173


L’annotation Context
– javax.ws.rs.ext.ContextResolver<T> : cette interface fournit le
contexte demandé aux classes ressources et autres fournisseurs.
– javax.servlet.http.HttpServletRequest : cette interface est utilisée
par le client pour accéder aux informations de la requête de la
servlet
– javax.servlet.http.HttpServletResponse : cette interface est
utilisée par le client pour accéder aux informations de la réponse
de la servlet
– javax.servlet.ServletContext : cette interface fournit les méthodes
à une servlet pour communiquer avec le conteneur de servlet
– javax.servlet.ServletConfig : cette interface contient les
informations de configuration de la servlet.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 151 / 173


L’annotation BeanParam
L’annotation @javax.ws.rs.BeanParam
permet d’injecter tous les paramètres dans
un seul objet bean. Cette annotation peut
être utilisée dans le champ d’une classe,
d’une ressource ou le paramètre d’une
méthode.
La classe bean peut avoir des champs
annotés.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 152 / 173


L’annotation BeanParam
public
public void
void createDepartment(
createDepartment( @FormParam("departmentId")
@FormParam("departmentId") short
short departmentId,
departmentId,
@FormParam("departmentName")
@FormParam("departmentName") String
String departmentName)
departmentName)

public
public class
class DepartmentBean
DepartmentBean {{
@FormParam("departmentId")
@FormParam("departmentId")
private
private short
short departmentId;
departmentId;
@FormParam("departmentName")
@FormParam("departmentName")
private
private String
String departmentName;
departmentName;
//// Getters
Getters et
et Setters
Setters
}}

@POST
@POST
public
public void
void createDepartment(@BeanParam
createDepartment(@BeanParam DepartmentBean
DepartmentBean deptBean)
deptBean)
{{
createDepartmentEntity(deptBean.getDepartmentId(),
createDepartmentEntity(deptBean.getDepartmentId(),
deptBean.getDepartmentName());
deptBean.getDepartmentName());
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 153 / 173


Le data binding avec JAX-RS
Lorsque l’on injecte les valeurs dans les
variables, JAX-RS suit un ensemble de règles
stricts :
Ces règles dépendent si l’on travaille avec
les types java connus ou bien avec les types
(classes) définis soi-même.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 154 / 173


Correspondance entre variables
et types java
A l’exécution, le framework va automatiquement détecter et copier
les valeurs des paramètres présents dans les variables en procédant à
la conversion des types. La règle s’applique à toutes les annotations
excepté @Context et elle marche pour :
– Tous les types primitifs exceptés char tels short, int, etc.
– Toutes les classes emballages excepté Char
– Toutes les classes avec un constructeur qui accepte un seul paramètre
de type String
– Toutes les classes avec une méthode statique appelée valueOf qui
prend en entrée un String
– Un implémentation enregistrée de ParamConverterProvider
– Si les paramètres contiennent plus d’une valeur, alors vous pouvez
utiliser java.util.List<T>,java.util.Set<T> ou java.util.SortedSet<T>
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 155 / 173
Correspondance entre variables
et types java
Il serait intéressant de savoir comment le
framework initialise les variables s’il ne
trouve aucune correspondance et qu’il
n’existe pas d’annotation @DefaultValue :
– Tous les types primitifs auront la valeur par
défaut en suivant les règles par défaut java ;
– Tous les objets seront initialisés à null
– List, Set et SortedSet seront des collections
vides (équivalent emptySet()).
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 156 / 173
Correspondance entre le corps
des messages et types
JAX-RS utilise javax.ws.rs.ext.MessageBodyReader
pour faire la correspondance entre le corps d’une
requête et un type java approprié.
De l’autre côté,
javax.ws.rs.ext.MessageBodyWriter est utilisé
pour faire la correspondance entre le type java et
le corps de la réponse.
Ces deux classes levent l’exception
javax.ws.rs.WebApplicationException s’elles ne
peuvent faire la conversion.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 157 / 173
Correspondance entre le corps
des messages et types
JAX-RS offre par défaut des types pour la conversion de la
plupart des types utilisés :
– byte[], java.lang.String, java.io.InputStream, java.io.Reader,
java.io.File, javax.activation.DataSource et
javax.xml.transform.Source
– javax.ws.rs.core.MultivalueMap<K, V> pour la lecture et l’écriture
des contenus de type application/x-www-form-urlencoded
– javax.xml.bind.JAXBElement pour la lecture et l’écriture des
contenus de type xml
– java.lang.Boolean, java.lang.Character et java.lang.Number pour
la lecture et l’écriture des booléens, des caractères et des
nombres qui sont présents dans les messages de type text/plain.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 158 / 173


Utilisation de JAXB pour la
correspondance
L’on peut utiliser JAXB (Java Architecture for XML Binding) si l’on veut avoir plus de
contrôle sur la façon dont se fait la conversion.
JAXB offre des annotations pour faire la correspondance entre XML et les classes Java
et vice versa.
– @javax.xml.bind.annotation.XmlRootElement utilisé au niveau de la classe.
– @javax.xml.bind.annotation.XmlAccessorType contrôle si le champ doit être sérialisé
ou pas. Les valeurs possibles sont :
» XmlAccessType.FIELD : tous les champs non statiques et non transient seront recopiés dans la
représentation XML ou JSON
» XmlAccessType.NONE : aucun champ n’est copié
» XmlAccessType.PROPERTY : toute pair de getter et setter sera recopié dans la représentation
XML ou JSON
» XmlAccessType.PUBLIC_MEMBER : tout champ public et toute pair de getter et setter
publique sont copiés dans la représentation XML et JSON
– @javax.xml.bind.XmlElement permet d’associer un champ à un élément XML ou JSON
– @javax.xml.bind.XmlTransient indique que la propriété ne doit pas être sérialisé.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 159 / 173


Les règles de précédence dans les
chemins
Il peut arriver que pour une URI donné plusieurs
mapping sont possible. JAX-RS règle simplement ce
problème en disant que la règle la plus spécifique gagne.
Plus spécifiquement les différents chemins sont triés :
– En utilisant le nombre de caractères du pattern dans
l’ordre décroissant ;
– La seconde clé du tri est le nombre de variable dans le
pattern par ordre décroissant
– La troisième clé est le nombre de template par défaut.
Un template par défaut est celui qui ne définit pas une
expression régulière ou dont l’expression est .+
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 160 / 173
Les règles de précédence dans les
chemins
Si les patterns suivants ont été triés en
suivant la règle énoncée tout à l’heure :
– /customers/{id}/{name}/address
– /customers/{id : .+}/address
– /customers/{id}/address
– /customers/{id : .+}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 161 / 173


Bean Validation et JAX-RS
Il est important de toujours se rassurer que les
données envoyés par le client sont conformes
à certaines règles.
La validation des beans est un mécanisme très
utilisé en Java.
A partir de JAX-RS 2.x, il est possible de valider
les éléments de la classe ressource ainsi que
les méthodes grâce à la bean validation.
– L’on peut aussi associer bean validation et JPA
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 162 / 173
Bean Validation et JAX-RS
La bean validation opère avec les annotations. Les plus
utilisées sont:
– @NotNull (@Null)
– @AssertFalse (@AssertTrue)
– @DecimalMax (@DecimalMin)
– @Digits
– @Max (@Min)
– @Future (@Past)
– @Pattern
– @Size
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 163 / 173
Mise en œuvre de JAX-RS dans
une application SpringBoot
Le cycle de vie des web services est géré par JAX-RS
et celui des autres composants est géré par Spring.
Il y’a donc un mismatch entre les deux types de
composants.
Il n’existe pas de solution standard pour ce
problème mais chaque implémente offre un
mécanisme pour gérer ce problème.
Avec Jersey, il faut créer un composant spring qui
étend la classe
org.glassfish.jersey.server.ResourceConfig
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 164 / 173
Déploiement d’une application
JAX-RS
Pour déployer une application JAX-RS, il
faut créer une classe qui étend la classe
javax.ws.rs.core.Application.
Il faut redéfinir la méthode public
Set<Class<?>> getClasses() et enregistrer
toutes les ressources.
Si vous utiliser Netbeans pour créer vos web
services, généralement l’éditeur va créer
automatiquement cette classe pour vous.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 165 / 173
Mise en œuvre de JAX-RS dans
une application SpringBoot
Une fois le composant créé, il faut
enrégistrer chaque ressource en appelant la
méthode register (de ResourceConfig).
@ApplicationPath("/api")
@ApplicationPath("/api")
@Component
@Component
public
public class
class MyConfig
MyConfig extends
extends ResourceConfig{
ResourceConfig{

public
public MyConfig()
MyConfig() {{
registerMyClasses();
registerMyClasses();
}}

private
private void
void registerMyClasses()
registerMyClasses() {{
register(DepartementResource.class);
register(DepartementResource.class);
register(UtilisateurResource.class);
register(UtilisateurResource.class);
register(EtudiantResource.class);
register(EtudiantResource.class);
}}
}}

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 166 / 173


Quelques règles à suivre
REST est un style d’architecture, et non une spécification,
pour la construction des webservices scalable.
Comme il ne s’agit pas d’une spécification, l’on peut
trouver plusieurs interprétation sur la façon dont ces
webservices doivent être construits.
Quelques règles à suivre :
– Identification des ressources du domaine d’application ;
– Transformations des opérations en utilisant les méthodes
HTTP ;
– Nommage des RESTful web services.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 167 / 173


Identification des ressources du
domaine d’application.
– Identifier tous objets du domaine d’application.
Généralement ces objets correspondent aux noms
dans la description du problème. Si nous travaillons
dans le domaine de la scolarité, les objets comme
étudiant et département sont les bons clients pour
être des objets.
– Parmi les objets, il faut identifier ceux qui peuvent
être manipuler en utilisant les opérations CRUD. Ces
objets sont les ressources. Il faudra distinguer les
ressources de haut niveau et d’autres qui seront
utilisées comme sous-ressource.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 168 / 173
Transformations des opérations
en utilisant les méthodes HTTP
Une fois les ressources identifiées, il faut faire la
correspondance entre les méthodes définies par la
ressource et les méthodes HTTP.
Les méthodes HTTP les plus utilisées sont GET,
POST, PUT et DELETE.
Il n’ya pas de correspondance d’un à un entre les
méthodes au niveau de la ressources et celle de
HTTP.
Comprendre les méthodes sûres et idempotentes
peut aider à faire une bonne correspondance.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 169 / 173
Transformations des opérations
en utilisant les méthodes HTTP
– Une méthode est idempotente si plusieurs exécution de la
même méthode produisent le même résultat
– Une méthode est sûre si elle ne change pas l’état de la
ressource.
Méthodes
Méthodes Idempotent ?
Idempotent ? Sûre ?
Sûre ?

GET,
GET, OPTIONS,
OPTIONS, HEAD
HEAD OUI
OUI OUI
OUI

POST,
POST, PATCH
PATCH NON
NON NON
NON

PUT,
PUT, DELETE
DELETE OUI
OUI NON
NON

ds

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 170 / 173


Transformations des opérations
en utilisant les méthodes HTTP
Une question importante est de savoir à
quel moment utiliser PUT ou POST.
– L’on utilise la méthode PUT pour créer ou
mettre à jour lorsque le client a tout le
contenu de la ressource.
– L’on utilise la méthode POST pour créer ou
mettre à jour si le client a un contenu partiel
de la ressource.

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 171 / 173


Nommage des RESTful web
services
Les ressources sont les éléments essentiels
des RESTful web services. Certains
conventions sont nécessaires :
– Utiliser les noms pour désigner les
ressources et les éléments du chemin au lieu
d’utiliser les verbes.
– Utiliser les noms au pluriel lorsque l’on a à
faire à une collections de ressources. Eviter
de mixer le pluriel et le masculin.
Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 172 / 173
Nommage des RESTful web
services
Ressource
Ressource GET
GET POST
POST PUT
PUT DELETE
DELETE

/etudiants/
/etudiants/ Récupérer
Récupérer Créer
Créer un
un Mise
Mise àà jour
jour de
de Supprimer
Supprimer tous
tous
tous
tous les
les nouvel
nouvel étudiant
étudiant tous
tous les
les les
les étudiants
étudiants
étudiants
étudiants étudiants
étudiants

/etudiants/{id}
/etudiants/{id} Récupérer
Récupérer le le Non
Non supporté
supporté Mise
Mise àà jour
jour de
de Supprimer
Supprimer
département
département l’étufiant
l’étufiant dont
dont l’étudiant
l’étudiant dont
dont
dont
dont l’id
l’id est
est 10
10 l’id
l’id est
est 10
10 l’id
l’id est
est 10
10

Projet tutoré – Douwe Vincent <douwevincent@yahoo.fr> 173 / 173

Vous aimerez peut-être aussi