Agile Tour 2013 : Montpellier tient toutes ses promesses!

Agile Tour 2013 : Montpellier tient toutes ses promesses!

agile-tour-montpellier-web1

La faculté d’éducation (IUFM) de Montpellier accueillait le vendredi 18 octobre dernier  l’agile tour édition 2013. Un événement dans la sphère de l’informatique qui a rassemblé plus de 350 participants, avides de connaître les clés de cette méthode montante. J’y étais, sous une triple casquette :  Sponsor avec Java-Project, organisateur, et participant. Voici les principaux points à retenir de cette journée riche d’enseignements, au cours de laquelle conférences pointues et ateliers ont permis de toucher du doigt l’essence de l’agilité.

agile-tour-2013-café

La journée débute par un café/viennoiseries. Bien utile pour voir les premières têtes et saluer des collègues ou amis venus eux aussi participer à l’événement.

agile-tour-2013-sponsor

Un peu avant 9h, direction l’amphithéâtre « Respect  » pour la présentation de la journée par l’équipe organisatrice, ainsi que le mot des sponsors auquel je prends part. J’en profite pour reprendre à mon compte  le thème de la journée « Travailler ensemble, autrement »: non, il ne concerne pas que les grosses entreprises. Bien au contraire,  il s’applique aussi aux indépendants. En effet, pour beaucoup d’entre nous,  l’expérimentation est une passion. Et l’agilité en est le cœur.

agile-tour-2013-keynote

Ensuite, place à la Keynote de Pablo, qui après s’être rodé à Toulouse et Marseille, nous expose « La horde Agile ». Utilisant le story-telling pour nous présenter le fonctionnement et les principes qui animent cette horde, il fait ensuite le lien avec l’agilité. Technique efficace, qui prouve ainsi que les pratiques vieilles de plusieurs millions d’années font partie de notre culture, et surtout de la culture Agile. Pablo fait aussi allusion au Tribal leadership, qui effectivement prend toute sa valeur dans le contexte de sa horde.

agile-tour-2013-tdd

Après la keynote, je me dirige vers l’atelier « L’apprentissage du TDD en coding-dojo » présenté par Xavier Nopre. Cet atelier, rassemble surtout des développeurs et se découpe en deux parties :

  1. Présentation du TDD, de son importance et de sa philosophie
  2. Atelier en binôme sur le Kata du score de tennis

Je n’avais jamais fait d’atelier TDD auparavant, et l’approche de Xavier, que l’on sent passionné, m’a convaincu. Le cycle de mise en place du TDD est relativement simple :

    1. Ecriture du test qui ne passe pas pour de bonnes raisons
    2. Ecriture du code de production pour faire passer le test au vert
    3. Refactoring du code

Lors de la deuxième partie de l’atelier, plusieurs d’entre nous ont participé à l’application de ce cycle. Démonstration implacable : la pratique aide grandement à aborder la philosophie.

agile-tour-2013-repas
Après cette matinée riche en nouvelles perspectives, un repas nous attend dans le jardin de l’IUFM. C’est le moment de profiter des petits fours, de la dégustation de vin du domaine de la Devèze et du fabuleux dessert, mais surtout de partager nos parcours du matin, et de discuter avec les orateurs.

agile-tour-2013-manager-sans-autorité
L’après-midi, je choisis de découvrir les conférences. J’opte pour la session de Guillaume Duquesnay : « Manager sans autorité : leçons apprises en animant Donjons et Dragon ». Ce coach agile présente sa quête vers le maître de jeu idéal, avec ses réactions face à des situations spécifiques. C’est au public de faire le pendant avec la manager, pour faire le lien entre sa présentation et la gestion d’un projet telle qu’on la connait.

agile-tour-2013-lean-startup
Pour finir la journée, je décide de participer à la conférence de Camille Roux, sur le Lean startup. Le rythme de la présentation est vraiment soutenu, et d’une excellente qualité. A l’évidence, Camille maîtrise son sujet à la perfection, et se retient de ne pas trop en dire pour ne pas dépasser son timing. On a donc vu les différentes étapes pour monter sa start-up, ainsi que les méthodes utilisées. Je retiens sa présentation du Business Model Canvas, que je détaillerai dans un prochain billet.

agile-tour-2013-pot-cloture
18h. L’agile tour Montpellier, c’est un total de 28 ateliers et conférences, des orateurs convaincus qui ouvrent le champ des possibles à des participants enthousiastes. Une journée dense, mais passionnante. Vivement l’édition 2014…

Clean Code

Clean Code

N’importe quel logiciel est constitué de code, nous pouvons créer des langages qui sont au plus près des besoins business, nous pouvons créer des outils qui nous aident à analyser et traduire ces exigences, mais il y aura toujours du code, et plus ce code est clean plus le logiciel est maintenable et évolutif.

Mais comment doit être un code clean?

Bad Code

Vous avez déjà été considérablement gêné par un mauvais code, vous avez sûrement fait face à cet obstacle plusieurs fois, mais pourquoi l’avez-vous écrit ? Si vous êtes développeurs, vous avez forcément écrit un jour du mauvais code, c’est inévitable, et les raisons sont multiples, et certainement bien claires dans vos têtes:

  • Essayer d’aller vite.
  • Pas de temps à perdre sur cette feature.
  • Vous n’avez pas eu le temps de faire un bon travail.
  • Votre patron serait en colère contre vous si vous preniez le temps de nettoyer votre code.
  • Peut-être que vous étiez fatigué de travailler sur ce programme et vous vouliez que ce soit fini.

Peu importent les raisons, le point commun à cette session de mauvais code est toujours une promesse malheureusement vite dite et vite oubliée, « je nettoierai plus tard ».

Une fois que notre code marche, on se dit : « un mauvais code qui marche, c’est mieux que rien », puis on passe à autre chose avec l’idée de nettoyer plus tard, sauf que Plus tard est égal à jamais.

Seulement voilà, plus les features s’accumulent, plus le mauvais code est présent, et moins votre logiciel est maintenable, ce qui se traduit par une productivité de l’équipe qui ne cesse de diminuer, et qui tend finalement vers zéro. Ce qui nous mène à la nécessité de réécrire le code, proprement cette fois ci, afin d’y voir plus clair

Clean code ?

Vous êtes bien conscient qu’un mauvais code est un obstacle important, et la seule façon d’aller vite est de garder le code propre.

La question qui se pose est « Comment pouvez-vous écrire du code propre », il n’est pas possible d’essayer d’écrire du code propre si vous ne savez pas ce que cela signifie!

  • Un code propre est avant tout un code qui doit être agréable à lire.
  • Un mauvais code permet au désordre de grandir! Plus on a des bouts de code mauvais, plus la qualité de notre logiciel tend vers la décadence.
  • Un code propre doit prêter attention aux détails et doit être est focalisé, chaque fonction, chaque classe, chaque objet expose une attitude simple qui reste entièrement indépendante, et non polluée par les détails non nécessaires.
  • Un code propre doit clairement exposer le problème à résoudre et ne doit contenir que ce qui est nécessaire.
  • Il y a une différence entre le code qui est facile à lire et le code qui est facile à changer, un code propre doit être facile à améliorer par d’autres personnes.
  • Un code non testé n’est pas un code propre, peu importe s’il est lisible et accessible.
  • Un code propre est un code qui a été pris en charge par quelqu’un qui a pris le temps de rester simple et ordonné.
  • Ne contient pas de duplication.
  • Un code propre est aussi un code qui réduit au maximum le nombre d’entités telles que les classes, méthodes, etc.
  • Évident, simple et convaincant: quand on lit un code propre, on ne doit dépenser trop d’effort pour le comprendre,
    Savoir si le code est propre ou mauvais n’est pas suffisant, comment peut-on écrire un code propre ?

Plusieurs techniques et bonnes pratiques faciles à adopter existent pour aider à écrire un code propre : nommage explicite de vos fonctions et variables – se passer de commentaires le plus possible, fractionnement de votre code en de multiples petites fonctions et bien d’autres, etc …

JPA 5ème partie

JPA 5ème partie

5. Méthodes de récupération des entités

L’accès aux données s’effectue selon 3 axes :

  • Chargement à la demande
  • Chargement des instances associées à l’exécution afin de permettre le chargement d’un réseau plus large que celui défini par défaut (amélioration des performances)
  • Optimisation du SQL généré

5.1. Le lazy loading

Le lazy loading = Chargement à la demande (le chargement est effectué au premier accès)

5.1.1. Comportements par défaut

  • Association @(One|Many)ToMany : LAZY
  • Association @(One|Many)ToOne : EAGER
  • Assocation @Basic : EAGER

5.1.2. Paramétrage du chargement

  • Elément « fetch » des associations permet de modifier le type de chargement
  • FetchType.LAZY ou FetchType.EAGER
  • Les éléments qui sont chargés en « LAZY » sont remplacés par des proxy

Lors de l’accès un proxy, ce dernier déclenche de manière suivantes les événements suivants :

  • Lecture dans le gestionnaire d’entité
  • Lecture dans le cache du second niveau (si configuré)
  • Interrogation en base de données si cela s’avère nécessaire

L’accès à un proxy en dehors d’un contexte de persistance (gestionnaire d’entité) engendre une erreur du type LazyLoadingException

5.1.3. Options avancées de chargement (hibernate)

  • @org.hibernate.annotations.LazyToOne : permet de définir quelle méthode utilisée pour effectuer le Lazy Loading. Il existe 3 paramètres possibles :
    • org.hibernate.annotations.LazyToOneOption.PROXY : utilise la génération de proxy dynamique (comportement par défaut)
    • org.hibernate.annotations.LazyToOneOption.NO_PROXY : utilise l’instrumentation bytecode
    • org.hibernate.annotations.LazyToOneOption.FALSE : l’association ne peut pas être Lazy
  • @org.hibernate.annotations.LazyCollection : permet de définir quelle méthode utiliser pour effectuer le Lazy loading sur les collections (ToMany). Il existe 3 paramètres possibles :
    • org.hibernate.annotations.LazyCollectionOption.TRUE : la collection est chargée lorsqu’on y accede (comportement par défaut)
    • org.hibernate.annotations.LazyCollectionOption.EXTRA : tout sera mis en jeu pour éviter le chargement de la collection (permet d’accéder à la taille de la collection sans charger les éléments de la collection)
    • org.hibernate.annotations.LazyCollectionOption.FALSE : l’association ne peut pas être Lazy
  • @org.hibernate.annotations.Fetch : définit quelle méthode utilisée lors du chargement de la collection
    • org.hibernate.annotations.FetchMode.SELECT : un select SQL est généré lorsque l’on accède à l’association (par défaut)
    • org.hibernate.annotations.FetchMode.SUBSELECT : permet de charger en une seule requête toutes les collections qui ont le même rôle (deux instances de Team, om et psg, les collections players ne sont pas chargées. si on accès à la collection om.players les deux collections seront chargées en une seule requête)
    • org.hibernate.annotations.FetchMode.JOIN : inhibe le paramétrage LAZY. La collection est chargée au même moment que l’entité racine en une seule requête à l’aide d’un join.

Synthèse des équivalences :

Annotation Méthode Lazy Méthode Fetch
@[One|Many]ToOne(LAZY) @LazyToOne(PROXY) @Fetch(SELECT)
@[One|Many]ToOne(EAGER) @LazyToOne(PROXY) @Fetch(JOIN)
@[One|Many]ToMany(LAZY) @LazyCollection(TRUE) @Fetch(SELECT)
@[One|Many]ToMany(EAGER) @LazyCollection(FALSE) @Fetch(JOIN)

5.1.4. Fetch vs Lazy

  • Lazy
    • Deux requêtes générées pour initialiser complète d’une association
    • La seconde requête n’est pas générer si l’association n’est pas parcourue
  • Fetch
    • Une seule requête générée
    • toutes les colonnes sont rapportées même si l’association n’est pas parcourue

5.1.5. Type de collection et performances

Collection Avantage Inconvénient
Collection (Bag) Collection très efficace dans le cas d’une association bidirectionnelle Efface tous les éléments d’une collection puis les recrée lors d’un ajout/retrait si l’association est unidirectionnelle
Set Performant pour ajout/retrait/modification si la collection est inverse L’association doit être chargée si on la manipule
List/Map Performant pour ajout/retrait/modification. Très performants dans le cas d’une association ManyToMany Difficulté de mise en œuvre sur association bidirectionnelle
Collection + @IdCollection Performant partout Nécessite une colonne technique

5.2. Techniques de récupération d’objets
5.2.1. Lecture par identifiant

  • methode find()
    • Retourne la valeur null si l’enregistrement n’existe pas
    • Chargement immédiat
  • méthode getReference()
    • Renvoie une exception si enregistrement inexistant (chargement/vérification lors de l’accès à la propriété)
    • Retourne un Proxy

5.2.2. EJB-QL : API Query

  • Obtention d’un objet Query : em.createQuery()
  • Méthodes :
    • getResultList() -> List
    • getSingleResult() -> Object

5.2.2.1. La clause Select
Requête simple :

select t from Team t

La requête retourne tous les objets Team

  • la structure : SELECT + FROM + WHERE (le select n’est pas obligatoire mais préférable)
  • On ne pense plus en terme de table mais en terme d’objet
  • Système d’alias

Récupérer certaines propriétés :

select t.id, t .name from Team t
  • Requête retourne un tableau d’objets
  • Identique pour les entités

Navigabilité dans le graphe d’objets :

select upper(player.name) from Player player
select distinct player.school from Player player

5.2.2.2. La clause From

  • Alias
  • Polymorphisme

Considérons le modèle suivant:
playerrookiesuperstar

select player from Player player

Retourne tous les objets persistants du type Player, Rookie, SuperStar

select o from Object o

Retourne tous les objets persistants de la base

  • Jointures explicites (internes, externes)
select team from Team team join team.coach
select team from Team team left join team.coach

5.2.2.3. La clause Where

  • Jointures implicites
select team from Team team where team.coach.name = :name
select player from Player player where player.team.coach.name = :name
  • Opérateurs de restrictions
=,<>,<,>,>=,between, not between, in et not in
  • L’utilisation de null et not null identique à SQL
  • Injection de critères restrictifs
Query query = em.createQuery("select player from Player player where player.name = :name");
query.setParameter("name", name);

Considérons le bean dto ayant pour proriétés teamName et playerName:

StringBuffer queryString = new StringBuffer();
queryString.append("select team from Team team join team.players player")
.append(" where team.name = :teamName")
.append(" and player.name = :playerName");
Query query = em.createQuery(queryString.toString());
query.setProperties(dto);
Query query = em.createQuery("select player from Player player where player.name in(:names)");
List parameters = new ArrayList();
parameters.add("toto");
parameters.add("titi");
query.setParameter("names", parameters)
  • Externalisation des requetes
    • @NamedQuery et @NamedQueries (à définir dans les entités)
    • orm.xml
  • Chargement des associations
    • Conserver le chargement par défaut
    • Précharger manuellement le réseau

Bonne pratique:

  • Précharger autant d’entités désirées avec l’association ToOne
  • Précharger une seule collection avec l’association ToMany (éviter le produit cartésien)
select team from Team team left join fetch team.players
  • Retourne les objets Team en chargent les éléments Player des objets Team
  • Traitement des doublons
  • Le problème du n+1

Considérons une entité qui contient une association ToMany contenant n éléments. Le chargement d’une telle entité engendre les conséquences suivantse:

  • Une requête pour l’entité principale
  • n requête pour les éléments de la collection

Le chargement forcé peut résoudre ce problème.

  • le batch fetching: @org.hibernate.annotations.BatchSize(int size): permet de diminuer ne nombre de requêtes générées par le n+1 (=> 1 + (size / n))
  • Charger plusieurs collections
    • Faire une requête par collection
    • Utilisation du cache

5.2.3. l’API Criteria (hibernate)

  • Fonctionne avec la session Hibernate
Session session = (Session)em.getDelegate();
  • Instanciation de Criteria
Session session = (Session)em.getDelegate();
Criteria criteria = session.createCriteria(Team.class)

L’exécution des requête se fait par l’invocation de la méthode list()

  • Objet Criterion (permet de composer la requête via Criteria)
  • Object Expression permet de créer des instanciations de Criterion (voir le javadoc de Criterion)
  • Associations avec Criteria
    • Utilisation de la méthode setFetchMode()
    • FetchMode.JOIN : chargement via un left join
    • FetchMode.SELECT : chargement de l’association via un select
    • FetchMode.DEFAULT: respecte la définition du mapping

5.2.4. Requête SQL native
Utilisation du SQL (utilisé lors de requêtes spécifiques non pris en compte par la génération SQL)

em.createNativeQuery()

5.2.5. Options avancées d’interrogation

5.2.5.1 Instanciation dynamique

StringBuffer sql = new StringBuffer();
sql.append("select new package.PlayerDTO(player.name, player.height) from Player player");
List result = em.createQuery(sql.toString()).getResultList();

Nom de la classe entièrement qualifiée et la classe utilisée n’est pas persistante.

5.2.5.2. Trier les résultats

Avec EJB-QL :

from Player player order by player.name desc

Avec Criteria :

session.createCriteria(Player.class).addOrder(Order.desc("name"))

5.2.5.3. Fonctions d’agrégation et groupe

count(), min(), max(), sum(), avg(), group by, having

5.2.5.4. Requêtes imbriquées

select team from Team team where :height = (select max(player.height) from team.players player)

all : l’ensemble des résultats doit vérifier la condition
any : au moins un des résultats doit vérifier la condition

5.2.5.5. La pagination

setFirstResult(int)
setMaxResult(int)
JPA (4ème partie) héritage et polymorphisme

JPA (4ème partie) héritage et polymorphisme

4. Héritage, polymorphisme et modèles complexe
4.1.                    Stratégie de mapping d’héritage et polymorphisme
Rappel : Polymorphisme = qui peut prendre plusieurs formes.
3 Solutions possibles pour le mapping

  • Une table pour une hiérarchie de classe
  • Une table par classe concrète
  • Une table par classe fille

On choisit le type d’héritage par l’annotation @Inherance

  • strategy=InheritanceType.ONLY_ONE_TABLE (ou SINGLE_TABLE)
  • strategy=InheritanceType.TABLE_PER_CLASS
  • strategy=InheritanceType.JOINED

4.1.1.      Une table pour une hiérarchie de classe

  • Toutes les propriétés de toutes les classes parentes et classes filles sont mappées dans la même table
  • Les instances sont différenciées par une colonne spéciale discriminante (permet de distinguer les différents types de la hiérarchie d’héritage côté relationnel)
  • L’identifiant est héritée de la classe mère

Exemple :

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="planetype",discriminatorType=DiscriminatorType.STRING)@DiscriminatorValue("Plane")
public class Plane { ... }@Entity
@DiscriminatorValue("A320")
public class A320 extends Plane { ... }

Avantage: Mise en œuvre très simple
Inconvénient : Contrainte nullable obligatoire pour les champs correspondant aux classes filles
4.1.2.      Une table par classe concrète

  • Une table pour chaque entité, et seulement les données de cette entité sont dans la table
  • La moins bonne des solutions côté performances

Exemple :

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Flight implements Serializable {

L’annotation @MappedSuperclass permet de mapper les métadonnées qui s’appliquent aux sous-classes.
Inconvénient : Ne respecte pas le polymorphisme
Exemple :

  • La requête polymorphique « select p from Produit » retournera tous les objets de type « AppareilPhoto » et « Aspirateur ».
  • Considérons le bout de code suivant :
@OneToMany
private Set&lt;Produit&gt; produits = new HashSet&lt;Produit&gt;

Ceci est une association dite polymorphique. JPA engendrera une erreur du fait que l’objet Produit n’est pas mappé.
4.1.3.      Une table par classe concrète par union
Cette stratégie est une variante de la précédente. Cette stratégie exploite l’union des tables lors de la récupération de données gérant ainsi le polymorphisme. La classe abstraite est noté @Entity
Avantage : les contraintes not-null sont possibles pour les champs correspondant aux classes filles.
Inconvénients :

  • Duplication des champs
  • Performances médiocres (requêtes SQL sur plusieurs tables en cas de requêtes polymorphiques)

4.1.4.      Une table par classe fille

  • Une table pour chaque entité, mais les propriétés communes sont regroupées
  • La clé étrangère référence la clé primaire de la table dont elle hérite
  • L’id est hérité de la classe mère

Exemple :

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Boat implements Serializable { ... }
@Entity
public class Ferry extends Boat { ... }
@Entity
@PrimaryKeyJoinColumn(name="BOAT_ID")
public class AmericaCupClass extends Boat { ... }

Avantage:

  • Les contraintes not-null sont possibles
  • Meilleurs performances que la stratégie « table par classe concrète »

Inconvenient:

  • Performances moins bonnes que la stratégie « table par hiérarchie de classes »

4.2.                    Mise en œuvre d’une relation bidirectionnelle
La navigabilité est possible sur les deux extrémités de l’association point de vue du modèle objet. Le modèle relationnel ne permet pas cette navigabilité.

  • Notion de relation principale
  • Notion de relation inverse
  • Implémentation de méthodes métiers dites de « cohérences »

Exemple :

@Entity
public class Team{
@Id
@GeneratedValue
private int id ;
@OneToMany(mappedBy=”team”)
private Set<Player> players = new HashSet<Player>(0);
public void addPlayer(Player p){
players.add(p);
p.setTeam(this);
}
…
}@Entity
public class Player {
@Id
@GeneratedValue
private int id;
@ManyToOne
@JoinColumn(name=”TEAM_ID”)
private Team team;
…
}

Les associations bidirectionnelles offrent un confort de navigabilité non négligeable mais n’est pas obligatoire.
4.3.Une conception sans limite
4.3.1.Collection de valeurs primitives
La spécification JPA ne permet ce genre de collection.

  • Utilisation de l’annotation hibernate @CollectionOfElements

Exemple :

@Entity
public class Coach {
@id
private int id
@org.hibernate.annotations.CollectionOfElements
private Set<String> niknames = new HashSet<String>()
}

Dans ce cas de figure, nous aurons une table COACH_NIKNAMES avec deux colonnes :

  • COACH_ID : clé étrangère vers la table COACH
  • ELEMENT, un nikname particulier

4.3.2.      Association OneToOne

  • Association par clé étrangère

Cette association est identique à l’association ManyToOne et déclarant la clé étrangère comme unique (cf 3.4.1.2).

  • Association par clé primaires

Les deux entités de l’association partagent leurs clés primaires, aucune colonne supplémentaire.
Comment générer les clés primaires ?

  • Génération automatique pour l’entité propriétaire de la relation
  • Génération automatique pour l’entité inverse (non gérer par l’implémentation JPA => utilisation de l’annotation GenericGenerator d’hibernate).

Exemple :

@Entity
public class Team {
@Id
@GeneratedValue
private int id ;
@OneToOne
@PrimaryKeyJoinColumn
private Coach coach ;
}
@Entity
public class Coach {
@Id
@org.hibernate.annotations.GenericGenerator(
name=  « generateur »
strategy =  « foreign »
parameters = {
@Parameter(name= « property », value= « team »)
}
)
private int id ;
@OneToOne(mappedBy= « coach »)
private Team team ;
}

4.3.3.      Association ManyToMany

  • Utilisation d’une table de liaison
@ManyToMany
@JoinTable(
name= « A_B »,
joinColumns = @JoinColumn(name= « A_ID », referencedColumnName= « ID »)
inverseJoinColumns = @JoinColumn(name= « B_ID », referencedColumnName= « ID »)
)
private Set<Team> teams = new HashSet<Team>()
JPA (Troisième partie)

JPA (Troisième partie)

3. Métadonnées et mapping des entités

Les annotations se placent soit sur les variables d’instance soit par ses accesseurs.

  • Si la variable d’instance est annotée on parle d’accès par le champ (la variable d’instance sera directement utilisée)
  • Si le getter est annoté on parle d’accès par la propriété, les accesseurs seront utilisés

Il est recommandé d’utiliser les accesseurs.

Une annotation est vue comme un stéréotype UML, les IDE s’en servent pour la génération de code etc…

3.1. Annotations de bases

3.1.1. @Entity

Définit une classe comme entité.

Attribut Valeur par défaut Type Description
name nom de la classe String

Paramètre la table primaire mappée. (par défaut l’entité est mappée à la table du même nom que la classe)3.1.2. @Table

Attribut Valeur par défaut Type Description
name nom de la classe qualifiée String
catalog nom du catalogue par défaut String
shema égal au schéma de l’utilisateur String

3.1.3. Mapper plusieurs tables en une seule entité

3.1.3.1. @SecondaryTable

Attribut Valeur par défaut Type Description
Name String nom de la table secondaire
pkJoinColumns les colonnes dont le nom est le même que celles composant la clé primaire de la table @PrimaryKeyJoinColumn
catalog String
schema String

3.1.3.2. @SecondaryTables

3.1.4. Identité relationnelle de l’entité

3.1.4.1. @Id (pour une clé primaire simple)

Aucun attribut mais généralement lié à l’annotation @GeneratedValue qui lui est dédiée

3.1.4.2. @GeneratedValue

Génération automatique de la valeur d’une clé primaire

Attribut Valeur par défaut Type Description
strategy GenerationType.AUTO GenerationTypeTABLE, SEQUENCE, IDENTITY, AUTO type de génération de valeur
generator String le nom du générateur

3.1.4.3. @SequenceGenerator

3.1.4.4. @TableGenerator

3.1.4.5. @IdClass

Cette annotation est utilisée pour une classe représentant une clé composée (Voir fonctionnalités de mapping avancées)

3.1.5. Propriétés persistantes par défaut

Toutes les propriétés d’une entité autre que les propriétés d’association sont par défaut persistantes. Leur annotation est donc optionnelle.

• Si le type est une classe annotée avec @Embeddable => Annotation automatique avec @Embedded

• Si le type est un object de type primitif (simple) => Annotation automatique avec @Basic

3.1.5.1. @Basic (propriété simple persistante)

Attribut Valeur par défaut Type Description
fetch FetchType.EAGER FetchType{LAZY,EAGER} nom de la colonne
optional true booléen la valeur de la propriété peut être null

3.1.5.2. @Column (paramétrage fin de la colonne)

Attribut Valeur par défaut Type Description
name Nom de la propriété annotée String nom de la colonne
unique False booléen contrainte unique
insertable True booléen colonne incluse dans les insert générés
updatable True booléen colonne incluse dans les update généres
length 255 int longueur de la colonne
precision 0 int précision pour un décimal
scale 0 int échelle pour un décimal

3.1.5.3. @Transient

3.1.5.4. @Lob

3.1.5.5. @Temporal

Persistance d’informations temporelles : utilisée pour la mapping de type Calendar et Date

Attribut Valeur par défaut Type Description
description TemporalType {DATE, TIME, TIMESTAMP}

3.1.5.6. @Enumerated

Persistance d’énumération : une énumération peut être mappée en tant que chaîne ou entier

Attribut Valeur par défaut Type Description
Value ORDINAL EnumType {ORDINAL, STRING}

3.1.5.7. @Version

Versionnement des entités : mise en place de la gestion de la concurrence optimiste.

3.2. Objets inclus

 Mapper deux classes liées par une relation ToOne à une seule table.

 Cycle de vie identique à l’entité à laquelle l’objet est associé

 Il s’agit d’une valeur

3.2.1. @Embeddable

Chaque propriété est stockée dans la même table primaire mappée à l’entité (ne contient pas d’association).

3.2.2. @Embedded

Référencer un objet inclus : spécifie qu’une propriété de l’entité est un objet inclus.

3.2.3. @EmbeddedId

Identique à @Embedded mais pour les identifiants (cette annotation remplace @Id).

3.2.4. @AttributeOverride

Surcharge le mapping d’un attribut

Attribut Valeur par défaut Type Description
name String nom de la propriété surchargée
column @Column définition de la colonne à utilisée

3.2.5. @AttributeOverrides

3.3. Jointure des tables

Deux types de jointures :

• une colonne (autre que la clé primaire) de la table A est clé étrangère vers la table B, plusieurs enregistrements de la table A peuvent être couplé à un meme enregistrement de la table B

• La clé primaire d’une table A est aussi clé étrangère vers une table B. Un enregistrement de la table A ne peut être couplé avec un seul enregistrement de la table B (relation OneToOne).

3.3.1. @JoinColumn

Definir une jointure entre deux tables.

Si aucune annotation @JoinColumn n’est utilisée, une simple colonne de jointure est adoptée en initialisant l’élément « name » selon les règles suivantes :

• Simple colonne de jointure : name = + ‘_’ +

• Utilisation d’une table de jointure : name = + ‘_’ +

Attribut Valeur par défaut Type Description
name String nom de la propriété surchargée
column @Column définition de la colonne à utilisée

3.3.2. @JoinColumns

Jointure effectuée sur plusieurs colonnes. Les éléments « name » et « refrencedColumnName » sont obligatoires.

Attribut Valeur par défaut Type Description
Value @JoinColumn[]

3.3.3. @PrimaryKeyJoinColumn

Dans le cadre d’une stratégie d’héritage joined ou dans le cas d’une association OneToOne les tables mappées doivent être jointes par leurs clés primaires (voir le chapitre sur l’héritage).

Attribut Valeur par défaut Type Description
name
  • Stratégie d’héritage joined : nom de la clé primaire de la table principale de la super-classe
  • Mapping @SecondaryTable : même nom de la colonne clé de la table principale
  • Mapping OneToOne : nom de la clé primaire pour la table de l’entité référencé
String
referencedColumnName nom de la colonne clé primaire de la table référencée String nom de la colonne référencée

3.3.4. @PrimaryKeyJoinColumns

Gestion de clés primaires composites.

3.4. Association d’entités

Contrairement au modèle relationnel, les relations d’association peuvent être bidirectionnelles.

• L’association principale est obligatoire

• L’association inverse est facultative

3.4.1. Association X–1

3.4.1.1. @ManyToOne

Spécifie une relation UML du type *–1

Description :

Attribut Valeur par défaut Type Description
targetEntity déduit du code Java Class Type de l’entité associé
cascade Aucune CascadeType[] {ALL, PERSIST, MERGE, REMOVE, REFRESH} Types d’opérations devant être effectuées en cascade sur l’entité associée
fetch

FetchType {EAGER, LAZY}

FetchType type de chargement (agressif, à la demande)
optional true booléen définit si l’association est optionnelle

Exemple :

Sur la classe Player : (Une team comporte plusieurs player => relation 1…*)

@ManyToOne(fetch=FetchType.LAZY)

@JoinColumn(name= « TEAM_ID »)

public Team getTeam() { return team ;}

3.4.1.2. @OneToOne

Spécifie une relation UML du type 1—1.

Deux possibilités possibles pour implémenter une association OneToOne :

  • Association fondée sur la clé étrangère
  • Association fondée sur le partage de la clé primaire des deux entités

1 Coach appartient à 1 équipe

Description :

Attribut Valeur par défaut Type Description
targetEntity déduit du code Java Class Type de l’entité associé
cascade Aucune CascadeType[] Types d’opérations devant être effectuées en cascade sur l’entité associée
fetch

FetchType {EAGER, LAZY}

FetchType type de chargement (agressif, à la demande)
optional true booléen définit si l’association est optionnelle
mappedBy String Obligatoire dans le cas d’une relation bidirectionnelle. Définit quelle propriété de l’entité associé gère la relation

Exemple d’association fondée sur une clé étrangère :

Sur la classe Team

@OneToOne

@JoinColumn(name= « COACH-ID », unique=true, nullable=false)

public Coach getCoach() {return coach;}

Sur la classe Coach

@OneToOne(mappedBy= “coach”)

public Team getTeam() { return team; }

Exemple d’association sur le partage de la clé primaire:

Sur la classe Team :

@Entity

public class Team {

@Id

Integer id

@OneToOne

@PrimaryKeyJoinColumn

Coach coach

}

Sur la classe Coach :

@Entity

public class Coach {

@Id

Integer id

}

3.4.2. Mapper les collections, association X–*

Collections possibles :

Set : ne contient pas de doublon

List : peut contenir des doublons, collection indexée

Map : association clé valeur

3.4.2.1. @OneToMany

Permet une relation UML du type 1–*

Attribut Valeur par défaut Type Description
targetEntity déduit du code Java Class Type de l’entité associé
cascade Aucune CascadeType[] Types d’opérations devant être effectuées en cascade sur l’entité associée
fetch

FetchType.LAZY

FetchType type de chargement (agressif, à la demande)
mappedBy String Obligatoire dans le cas d’une relation bidirectionnelle. Définit quelle propriété de l’entité associé gère la relation

3.4.2.2. @JoinTable

Définit une table de jointure, utilisé dans les relations OneToMany unidirectionnel et ManyToMany

Attribut Valeur par défaut Type Description
name Déduit des deux entités String Nom de la table de jointure
joinColumns idem que @JoinColumn @JoinColumn[] Colones portant la clé étrangère de la table qui référence la table primaire de l’entité propriétaire de l’association
inverseJoinColumns

idem que @JoinColumn

@JoinColumn Colonnes portant la clé étrangère de la table qui référence la table primaire de l’entité non propriétaire de la relation

Exemple :

@JoinTable(

joinColumns = @JoinColumn(name= « TEAM_ID », referencedColumnName= « ID »),

inverseJoinColumns=@JoinColumn(name= “GAME_ID”, referencedColumnName= « ID »)

)

3.4.2.3. @ManyToMany

Permet une association du type UML *–*, caractérisée par 2 extrémités, 1 principale (dite propriétaire) et 1 secondaire (dite inverse). La relation inverse est facultative.

Attribut Valeur par défaut Type Description
targetEntity déduit du code Java Class Type de l’entité associé
cascade Aucune CascadeType[] Types d’opérations devant être effectuées en cascade sur l’entité associée
fetch

FetchType {EAGER, LAZY}

FetchType type de chargement (agressif, à la demande)
mappedBy String Obligatoire dans le cas d’une relation bidirectionnelle. Utilisé par l’entité inverse.

Exemple :

Sur la classe Team :

@ManyToMany

@JoinTable(name= « TEAM_GAME »)

public Set getGames() { return games; }

Sur la classe Game:

@ManyToMany(mappedBy= “games”)
public Set getTeams() { return teams;}

3.4.2.4. @OrderBy

Permet de trier les éléments d’une collection lors du chargement (ordre non garanti lors de la manipulation de la collection)

JPA (Deuxième partie)

JPA (Deuxième partie)

2°) Démarrer avec JPA:

Architecture autour :

JPA_archi

  • Une application dispose d’entités dont la persistance est gérée par un gestionnaire d’entités. Une entité est une classe persistante (POJO : Plain Old Java Object) ou son instance. Un objet qui n’est pas persistant est dit Transient.
  • Un gestionnaire d’entités (EntityManager) s’obtient via une EntityManagerFactory. Il effectue des opérations sur la base de données. Ces opérations se déroulent au sein d’une transaction. Un gestionnaire d’entités est lié à la transaction en cours.
  • La création d’une EntityManagerFactory est tres lourde (analyse des métadonnées etc…). Elle est généralement stockée dans un singleton. Cette factory est threadsafe (peut être utilisée dans plusieurs traitements en parallèles).
  • Le gestionnaire d’entité n’est pas threadsafe mais il est peu couteux à instancier.

2.1.                    Les entités

  • Entité = Classe ou instance persistante (POJO)
  • Caractéristique d’une entité
    • Un constructeur sans argument
    • Une clé (l’unicité métier) : clé artificielle, aucun sens métier
    • Propriétés (getters/setters)
    • Comportement (méthode métier)
    • Classe non finale
    • Implémentation de l’interface Serializable
    • méthodes equals() et hashCode()

Les méthodes equals et hascode() sont-ils obligatoires ?

  • Si utilisation des clés composées : OBLIGATOIRE
  • Si appel à deux entités de deux gestionnaires d’entités différents : OBLIGATOIRE
  • Si l’objet est utilisé dans des Map ou Set : fortement conseillé
  • Dans les autres cas la redéfinition de ces méthodes n’est pas obligatoire mais recommandé

Note : Ne pas implémenter la méthode equals en se référençant à la clé (pb avec un objet transient qui devient persistant)

2.2.                    Cycle de vie d’un objet

  • Objet persistant : Un objet qui possède son image dans le datastore (ex : BD). L’objet est surveillé par « un traqueur » qui garantit les modifications apportées (rôle du gestionnaire d’entités)
  • Objet transient : Un objet qui ne possède pas son image dans le datastore, objet temporaire.
  • Objet détaché : Un objet qui possède son image dans le datastore mais qui échappe à la surveillance du gestionnaire d’entités.

Etats d’un objet :

etatobject

2.4.                    Le gestionnaire d’entités

  • Le gestionnaire d’entités = EntityManager
  • L’utilisation d’un gestionnaire d’entité se fait dans une transaction

Une instance d’EntityManager est associée avec un contexte de persistance. Ce contexte est un ensemble d’instances d’entités pour lequel, pour n’importe quelle entité, il existe une unique instance de l’entité.

2.4.1.      Actions du gestionnaire d’entité

  • Récupération d’une entité
    • find(Class, id)
    • getReference(Class, id) : la base de donnée n’est pas consultée, un proxy est retournée. (l’accès à un attribut (autre que l’identifiant) de cet objet engendrera la lecture dans la base de donnée)
    • Requête EJB-QL (HQL)
  • Rendre une nouvelle instance persistante
    • persist()
  • Rendre persistantes les modifications d’une instance persistante
    • Rien à faire, la modification de l’instance engendre la modification
    • Système de flush (les modifications sont exécutées le plus tard possible, lors du commit de la transaction)
  • Rendre persistantes les modifications d’une instance détachée
    • merge() : renvoie l’instante persistante
  • Détacher une instance persistante
    • Fermer le gestionnaire : close()
    • Vider le gestionnaire : clear()
    • Détacher une instance particulière : evict(obj) (spécifique à hibernate)

Conséquences de détacher une instance persistante : Aucune modification ne sera rendue persistante et l’ accès à un proxy engendrera une erreur

  • Enlever un objet
    • remove()

instancespersistantes

JPA (Première Partie)

JPA (Première Partie)

Première partie d’un tutoriel JPA, on  pose les bases :

Persistance et mapping objet-relationnel
1°) Principes de la persistance
La persistance non transparente : Délègue au développeur la responsabilité de coder la persistance des objets de l’application.

Conséquences :

  • Gestion de la connexion, écriture manuelle des requêtes, gestion des exceptions
  • Gestion des transactions manuellement, pas de cache, ni concurrence, ni clé primaire etc…
  • Création des beans en appelant les setters/getters
  • Requêtes SQL fortement lié au type de la base de données
  • Problématique des requetes avec un graphe d’objets (appliquer la modification d’un objet parent aux fils…)

La persistance transparente : Utilisation d’un framework de persistance.
Conséquences :

  • La gestion de la persistance ne concerne plus le développeur
  • Facilité et économie de code
  • Gestion de la concurrence, du cache, langage d’interrogation des objets…
  • « Indépendance » avec la base de données
  • Notion de persistance par accessibilité « persistence by reachability) (l’instance racine sera rendue persistance et les instances associées pourront être rendue persistantes ou non)

2°) Le mapping objet-relationnel
Le principe consiste à décrire une correspondance entre un schéma de base de données et un modèle de classes.

  • Utilisation des métadonnées dans les sources (annotations)
  • Possibilité d’utiliser des fichiers de mapping XML (souvent propriétaire)
  • Définir les relations entre les instances du modèle (notion de navigabilité, non présente dans un modèle relationnel)

Avantage des annotations par rapport aux fichiers de mapping :

  • Standardisation et portables
  • Moins verbeuses que le XML
  • Bénéficient de la phase de compilation