Accueil > Java EE > Mapping en JPA 1.0 d’une table de jointure ayant des colonnes supplémentaires

Mapping en JPA 1.0 d’une table de jointure ayant des colonnes supplémentaires

J’ai travaillé sur un prototype d’application qui gère les collaborateurs en fonction de leurs connaissances. Lorsque j’ai commencé à mapper le modèle de données, je me suis trouvé face à une problématique dont j’ignorais la solution : Comment mapper, en JPA (Java Persistence API),  une table de jointure comportant des colonnes supplémentaires en plus des colonnes de clés étrangères constituant sa clé primaire ?Lorsque j’ai posé la question autour de moi, la meilleure réponse était : c’est une belle question pour StackOverFlow. En cherchant sur internet je n’ai malheureusement trouvé que des ébauches de solutions…

Quelques heures de travail et quelques lignes de codes plus tard, voici une solution simple et élégante… Je détaillerai cette démarche dans deux billets :

  • Le premier décrit une solution basée sur la version 1.0 de l’API
  • Le second propose  de s’appuyer sur une annotation spécifique à JPA 2.0

Description de la problématique

 

Figure 1

La figure 1 représente le modèle de données de l’exemple de ce billet. Il s’agit d’une relation many-to-many entre des employés (collaborateurs) et des concepts (exemples : ORM, IOC …). Un employé (mappé par une entité Employe) peut avoir dans sa boite à outils plusieurs concepts (mappés par l’entité Concept). Cet employé ne sera pas le seul à connaître un concept donné. Enfin une note déterminera le niveau de maîtrise d’un concept par un employé.

Quelques éléments de réflexion…

L’idée, reprise différemment dans les deux solutions, est de mapper cette relation many-to-many entre Employe et Concept avec une entité intermédiaire EmployeConcept. Cette dernière représente la table de jointure en Java. Ainsi on aura une relation ManyToOne d’EmployeConcept vers Employe et une autre relation ManyToOne d’EmployeConcept vers Concept. (Avec OneToMany dans Employe et Concept pour des avoir relations bidirectionnelles)

A partir de la table de jointure EMPLOYE_CONCEPT, on peut dégager  deux points essentiels (que je vais analyser séparément) :

  • La clé primaire de cette table  est une clé composite. Elle est composée des deux champs EC_EMP_ID et EC_CPT_ID. Mapper ce type de clé en JPA (1.0 et 2.0) revient à encapsuler les champs qui composent la clé dans une classe. Puis l’entité faisant référence à cette classe a deux façons de le faire. Le Listing 1 montre  celle qui répond le mieux à notre besoin.
    <strong>Listing 1</strong>
    <span style="color: #808080;">01</span> <span style="color: #7f0055;"><strong>package </strong></span><span style="color: #000000;">com.de.gc.service.scheduler;</span>
    <span style="color: #808080;">02</span>
    <span style="color: #808080;">03</span> <span style="color: #646464;">@Embeddable</span>
    <span style="color: #808080;">04</span> <span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">EmployeConceptId </span><span style="color: #7f0055;"><strong>implements </strong></span><span style="color: #000000;">Serializable </span><span style="color: #000000;">{</span>
    <span style="color: #808080;">05</span>
    <span style="color: #808080;">06</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Column</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"ec_emp_id"</span><span style="color: #000000;">)</span>
    <span style="color: #808080;">07</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Long employeId;</span>
    <span style="color: #808080;">08</span>
    <span style="color: #808080;">09</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Column</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"ec_cpt_id"</span><span style="color: #000000;">)</span>
    <span style="color: #808080;">10</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Long conceptId;</span>
    <span style="color: #808080;">11</span>
    <span style="color: #808080;">12</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>public </strong></span><span style="color: #000000;">EmployeConceptId</span><span style="color: #000000;">() {</span>
    <span style="color: #808080;">13</span>
    <span style="color: #808080;">14</span> <span style="color: #ffffff;">    </span><span style="color: #000000;">}</span>
    <span style="color: #808080;">15</span>
    <span style="color: #808080;">16</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>public </strong></span><span style="color: #000000;">EmployeConceptId</span><span style="color: #000000;">(</span><span style="color: #000000;">Long employeId, Long conceptId</span><span style="color: #000000;">) {</span>
    <span style="color: #808080;">17</span> <span style="color: #ffffff;">        </span><span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">.employeId = employeId;</span>
    <span style="color: #808080;">18</span> <span style="color: #ffffff;">        </span><span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">.conceptId = conceptId;</span>
    <span style="color: #808080;">19</span> <span style="color: #ffffff;">    </span><span style="color: #000000;">}</span>
    <span style="color: #808080;">20</span>
    <span style="color: #808080;">21</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Override</span>
    <span style="color: #808080;">22</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>public </strong></span><span style="color: #7f0055;"><strong>boolean </strong></span><span style="color: #000000;">equals</span><span style="color: #000000;">(</span><span style="color: #000000;">Object obj</span><span style="color: #000000;">) {</span>
    <span style="color: #808080;">23</span> <span style="color: #ffffff;">        </span><span style="color: #000000;">?</span>
    <span style="color: #808080;">24</span> <span style="color: #ffffff;">    </span><span style="color: #000000;">}</span>
    <span style="color: #808080;">25</span>
    <span style="color: #808080;">26</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Override</span>
    <span style="color: #808080;">27</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>public </strong></span><span style="color: #7f0055;"><strong>int </strong></span><span style="color: #000000;">hashCode</span><span style="color: #000000;">() {</span>
    <span style="color: #808080;">28</span> <span style="color: #ffffff;">        </span><span style="color: #000000;">?</span>
    <span style="color: #808080;">29</span> <span style="color: #ffffff;">    </span><span style="color: #000000;">}</span>
    <span style="color: #808080;">30</span> <span style="color: #000000;">}</span>
    <span style="color: #808080;">31</span>
    <span style="color: #808080;">32</span> <span style="color: #646464;">@Entity</span>
    <span style="color: #808080;">33</span> <span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">EmployeConcept </span><span style="color: #000000;">{</span>
    <span style="color: #808080;">34</span>
    <span style="color: #808080;">35</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@EmbeddedId</span>
    <span style="color: #808080;">36</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">EmployeConceptId id;</span>
    <span style="color: #808080;">37</span>
    <span style="color: #808080;">38</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Column</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"ec_note"</span><span style="color: #000000;">)</span>
    <span style="color: #808080;">39</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #7f0055;"><strong>int </strong></span><span style="color: #000000;">note;</span>
    <span style="color: #808080;">40</span> <span style="color: #000000;">}</span>

Elle consiste à embarquer dans l’entité (EmployeConcept) un champ, id. Cet id aura comme type la classe EmployeConceptId. (Dans la suite du billet je ferai référence à cette classe par classe id). On annote ensuite cette classe avec @Embeddable et le champ id de l’entité avec @EmbeddedId. [NDLR : Combinaison d’un @Embedded et d’un @Id en même temps ; logique non ?]. Enfin, des attributs employeId et conceptId sont déclarés dans la classe id et mappés aux colonnes EC_EMP_ID et  EC_CPT_ID grâce à l’annotation @Column. Attention cette classe doit obligatoirement, redéfinir les méthodes equals() et hashCode() afin d’être persistée et utilisée comme clé par l’outil d’ORM (Hibernate par exemple). Elle doit aussi implémenter l’interface Serializable et fournir un constructeur par défaut.

  • Les champs EC_EMP_ID et  EC_CPT_ID de la table EMPLOYE_CONCEPT sont les clés étrangères qui référencent respectivement les tables EMPLOYE(EMP_ID) et CONCEPT(CPT_ID). Le mapping de ce point de vue est l’un des plus communs comme le montre le Listing 2.
    <strong>Listing 2</strong>
    <span style="color: #808080;">01</span> <span style="color: #646464;">@Entity</span>
    <span style="color: #808080;">02</span> <span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">EmployeConcept </span><span style="color: #000000;">{</span>
    <span style="color: #808080;">03</span>
    <span style="color: #808080;">04</span> <span style="color: #ffffff;">    </span><span style="color: #3f7f5f;">// mapping de l'id</span>
    <span style="color: #808080;">05</span>
    <span style="color: #808080;">06</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@ManyToOne</span>
    <span style="color: #808080;">07</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@JoinColumn</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"ec_emp_id"</span><span style="color: #000000;">)</span>
    <span style="color: #808080;">08</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Employe employe;</span>
    <span style="color: #808080;">09</span>
    <span style="color: #808080;">10</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@JoinColumn</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"ec_cpt_id"</span><span style="color: #000000;">)</span>
    <span style="color: #808080;">11</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@ManyToOne</span>
    <span style="color: #808080;">12</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Concept concept;</span>
    <span style="color: #808080;">13</span>
    <span style="color: #808080;">14</span> <span style="color: #ffffff;">    </span><span style="color: #3f7f5f;">// Getters and setters</span>
    <span style="color: #808080;">15</span> <span style="color: #000000;">}</span>
    <span style="color: #808080;">16</span>
    <span style="color: #808080;">17</span> <span style="color: #646464;">@Entity</span>
    <span style="color: #808080;">18</span> <span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">Employe </span><span style="color: #000000;">{</span>
    <span style="color: #808080;">19</span>
    <span style="color: #808080;">20</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Id</span>
    <span style="color: #808080;">21</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Column</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"emp_id"</span><span style="color: #000000;">)</span>
    <span style="color: #808080;">22</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Long id;</span>
    <span style="color: #808080;">23</span>
    <span style="color: #808080;">24</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@OneToMany</span><span style="color: #000000;">(</span><span style="color: #000000;">mappedBy = </span><span style="color: #2a00ff;">"employe"</span><span style="color: #000000;">)</span>
    <span style="color: #808080;">25</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Set&lt;EmployeConcept&gt; concepts = </span><span style="color: #7f0055;"><strong>new </strong></span><span style="color: #000000;">HashSet&lt;EmployeConcept&gt;</span><span style="color: #000000;">()</span><span style="color: #000000;">;</span>
    <span style="color: #808080;">26</span>
    <span style="color: #808080;">27</span> <span style="color: #ffffff;">    </span><span style="color: #3f7f5f;">// Getters and setters</span>
    <span style="color: #808080;">28</span> <span style="color: #000000;">}</span>
    <span style="color: #808080;">29</span>
    <span style="color: #808080;">30</span> <span style="color: #646464;">@Entity</span>
    <span style="color: #808080;">31</span> <span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">Concept </span><span style="color: #000000;">{</span>
    <span style="color: #808080;">32</span>
    <span style="color: #808080;">33</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Id</span>
    <span style="color: #808080;">34</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Column</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"cpt_id"</span><span style="color: #000000;">)</span>
    <span style="color: #808080;">35</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Long id;</span>
    <span style="color: #808080;">36</span>
    <span style="color: #808080;">37</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@OneToMany</span><span style="color: #000000;">(</span><span style="color: #000000;">mappedBy = </span><span style="color: #2a00ff;">"concept"</span><span style="color: #000000;">)</span>
    <span style="color: #808080;">38</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Set&lt;EmployeConcept&gt; employes = </span><span style="color: #7f0055;"><strong>new </strong></span><span style="color: #000000;">HashSet&lt;EmployeConcept&gt;</span><span style="color: #000000;">()</span><span style="color: #000000;">;</span>
    <span style="color: #808080;">39</span>
    <span style="color: #808080;">40</span> <span style="color: #ffffff;">    </span><span style="color: #3f7f5f;">// Getters and setters</span>
    <span style="color: #808080;">41</span> <span style="color: #000000;">}</span>

Les trois tables sont mappées respectivement par les entités EmployeConcept, Employe et Concept.

On définit deux relations ManyToOne :

  • De EmployeConcept vers Employe (où la colonne de jointure, ou @JoinColumn, estEC_EMP_ID)
  • De EmployeConcept vers Concept (où la colonne de jointure ,ou @JoinColumn, est EC_CPT_ID)

NB : À ce stade on fait abstraction du fait que les colonnes EC_EMP_ID et EC_CPT_ID forment aussi la clé primaire composite. (J’ai fait exprès d’omettre le mapping de l’id dans EmployeConcept)

Ainsi on a résolu, chaque point séparément. La solution de la problématique de ce billet, consiste à combiner ces deux solutions.

En fusionnant les deux listing précédents on remarque que les deux colonnes ec_emp_id et ec_cpt_id sont mappées deux fois

  • Dans la classe id EmployeConceptId avec @Column
  • Dans EmployeConcept comme @JoinColumn pour la @ManyToOne

Résoudre ce conflit sera la solution de notre problématique.

La solution en JPA 1.0

Avec la version 1.0 de l’API JPA, la solution consiste à faire en sorte que les relations ManyToOne soient en lecture seule. Le Listing 3 montre cette solution où on n’utilise que des annotations JPA 1.0.

<strong>Listing 3</strong>
<span style="color: #808080;">01</span> <span style="color: #646464;">@Embeddable</span>
<span style="color: #808080;">02</span> <span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">EmployeConceptId </span><span style="color: #7f0055;"><strong>implements </strong></span><span style="color: #000000;">Serializable </span><span style="color: #000000;">{</span>
<span style="color: #808080;">03</span>
<span style="color: #808080;">04</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Column</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"ec_emp_id"</span><span style="color: #000000;">)</span>
<span style="color: #808080;">05</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Long employeId;</span>
<span style="color: #808080;">06</span>
<span style="color: #808080;">07</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Column</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"ec_emp_id"</span><span style="color: #000000;">)</span>
<span style="color: #808080;">08</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Long conceptId;</span>
<span style="color: #808080;">09</span>
<span style="color: #808080;">10</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>public </strong></span><span style="color: #000000;">EmployeConceptId</span><span style="color: #000000;">() {</span>
<span style="color: #808080;">11</span>
<span style="color: #808080;">12</span> <span style="color: #ffffff;">    </span><span style="color: #000000;">}</span>
<span style="color: #808080;">13</span>
<span style="color: #808080;">14</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>public </strong></span><span style="color: #000000;">EmployeConceptId</span><span style="color: #000000;">(</span><span style="color: #000000;">Long employeId, Long conceptId</span><span style="color: #000000;">) {</span>
<span style="color: #808080;">15</span> <span style="color: #ffffff;">        </span><span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">.employeId = employeId;</span>
<span style="color: #808080;">16</span> <span style="color: #ffffff;">        </span><span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">.conceptId = conceptId;</span>
<span style="color: #808080;">17</span> <span style="color: #ffffff;">    </span><span style="color: #000000;">}</span>
<span style="color: #808080;">18</span>
<span style="color: #808080;">19</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>public </strong></span><span style="color: #7f0055;"><strong>boolean </strong></span><span style="color: #000000;">equals</span><span style="color: #000000;">(</span><span style="color: #000000;">Object obj</span><span style="color: #000000;">) {</span>
<span style="color: #808080;">20</span> <span style="color: #ffffff;">        </span><span style="color: #000000;">?</span>
<span style="color: #808080;">21</span> <span style="color: #ffffff;">    </span><span style="color: #000000;">}</span>
<span style="color: #808080;">22</span>
<span style="color: #808080;">23</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>public </strong></span><span style="color: #7f0055;"><strong>int </strong></span><span style="color: #000000;">hashCode</span><span style="color: #000000;">() {</span>
<span style="color: #808080;">24</span> <span style="color: #ffffff;">        </span><span style="color: #000000;">?</span>
<span style="color: #808080;">25</span> <span style="color: #ffffff;">    </span><span style="color: #000000;">}</span>
<span style="color: #808080;">26</span> <span style="color: #000000;">}</span>
<span style="color: #808080;">27</span>
<span style="color: #808080;">28</span> <span style="color: #646464;">@Entity</span>
<span style="color: #808080;">29</span> <span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">EmployeConcept </span><span style="color: #000000;">{</span>
<span style="color: #808080;">30</span>
<span style="color: #808080;">31</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@EmbeddedId</span>
<span style="color: #808080;">32</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">EmployeConceptId id;</span>
<span style="color: #808080;">33</span>
<span style="color: #808080;">34</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@ManyToOne</span>
<span style="color: #808080;">35</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@JoinColumn</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"ec_emp_id"</span><span style="color: #000000;">, insertable = false, updatable = </span><span style="color: #7f0055;"><strong>false</strong></span><span style="color: #000000;">)</span>
<span style="color: #808080;">36</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Employe employe;</span>
<span style="color: #808080;">37</span>
<span style="color: #808080;">38</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@ManyToOne</span>
<span style="color: #808080;">39</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@JoinColumn</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"ec_cpt_id"</span><span style="color: #000000;">, insertable = false, updatable = </span><span style="color: #7f0055;"><strong>false</strong></span><span style="color: #000000;">)</span>
<span style="color: #808080;">40</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Concept concept;</span>
<span style="color: #808080;">41</span>
<span style="color: #808080;">42</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>public </strong></span><span style="color: #000000;">EmployeConcept</span><span style="color: #000000;">(</span><span style="color: #000000;">Employe employe, Concept concept, </span><span style="color: #7f0055;"><strong>int </strong></span><span style="color: #000000;">note</span><span style="color: #000000;">) {</span>
<span style="color: #808080;">43</span> <span style="color: #ffffff;">        </span><span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">.employe = employe;</span>
<span style="color: #808080;">44</span> <span style="color: #ffffff;">        </span><span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">.concept = concept;</span>
<span style="color: #808080;">45</span>
<span style="color: #808080;">46</span> <span style="color: #ffffff;">        </span><span style="color: #000000;">employe.getConcepts</span><span style="color: #000000;">()</span><span style="color: #000000;">.add</span><span style="color: #000000;">(</span><span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>
<span style="color: #808080;">47</span> <span style="color: #ffffff;">        </span><span style="color: #000000;">concept.getEmployes</span><span style="color: #000000;">()</span><span style="color: #000000;">.add</span><span style="color: #000000;">(</span><span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>
<span style="color: #808080;">48</span>
<span style="color: #808080;">49</span> <span style="color: #ffffff;">        </span><span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">.id = </span><span style="color: #7f0055;"><strong>new </strong></span><span style="color: #000000;">EmployeConceptId</span><span style="color: #000000;">(</span><span style="color: #000000;">concept.getId</span><span style="color: #000000;">()</span><span style="color: #000000;">, employe.getId</span><span style="color: #000000;">())</span><span style="color: #000000;">;</span>
<span style="color: #808080;">50</span> <span style="color: #ffffff;">        </span><span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">.note = note;</span>
<span style="color: #808080;">51</span> <span style="color: #ffffff;">    </span><span style="color: #000000;">}</span>
<span style="color: #808080;">52</span>
<span style="color: #808080;">53</span> <span style="color: #ffffff;">    </span><span style="color: #3f7f5f;">// pas de setter pour employe et concept</span>
<span style="color: #808080;">54</span> <span style="color: #000000;">}</span>
<span style="color: #808080;">55</span>
<span style="color: #808080;">56</span> <span style="color: #646464;">@Entity</span>
<span style="color: #808080;">57</span> <span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">Employe </span><span style="color: #000000;">{</span>
<span style="color: #808080;">58</span>
<span style="color: #808080;">59</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Id</span>
<span style="color: #808080;">60</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Column</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"emp_id"</span><span style="color: #000000;">)</span>
<span style="color: #808080;">61</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Long id;</span>
<span style="color: #808080;">62</span>
<span style="color: #808080;">63</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@OneToMany</span><span style="color: #000000;">(</span><span style="color: #000000;">mappedBy = </span><span style="color: #2a00ff;">"employe"</span><span style="color: #000000;">)</span>
<span style="color: #808080;">64</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Set&lt;EmployeConcept&gt; concepts = </span><span style="color: #7f0055;"><strong>new </strong></span><span style="color: #000000;">HashSet&lt;EmployeConcept&gt;</span><span style="color: #000000;">()</span><span style="color: #000000;">;</span>
<span style="color: #808080;">65</span>
<span style="color: #808080;">66</span> <span style="color: #ffffff;">    </span><span style="color: #3f7f5f;">// Getters and setters</span>
<span style="color: #808080;">67</span> <span style="color: #000000;">}</span>
<span style="color: #808080;">68</span>
<span style="color: #808080;">69</span> <span style="color: #646464;">@Entity</span>
<span style="color: #808080;">70</span> <span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">Concept </span><span style="color: #000000;">{</span>
<span style="color: #808080;">71</span>
<span style="color: #808080;">72</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Id</span>
<span style="color: #808080;">73</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@Column</span><span style="color: #000000;">(</span><span style="color: #000000;">name = </span><span style="color: #2a00ff;">"cpt_id"</span><span style="color: #000000;">)</span>
<span style="color: #808080;">74</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Long id;</span>
<span style="color: #808080;">75</span>
<span style="color: #808080;">76</span> <span style="color: #ffffff;">    </span><span style="color: #646464;">@OneToMany</span><span style="color: #000000;">(</span><span style="color: #000000;">mappedBy = </span><span style="color: #2a00ff;">"concept"</span><span style="color: #000000;">)</span>
<span style="color: #808080;">77</span> <span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Set&lt;EmployeConcept&gt; employes = </span><span style="color: #7f0055;"><strong>new </strong></span><span style="color: #000000;">HashSet&lt;EmployeConcept&gt;</span><span style="color: #000000;">()</span><span style="color: #000000;">;</span>
<span style="color: #808080;">78</span>
<span style="color: #808080;">79</span> <span style="color: #ffffff;">    </span><span style="color: #3f7f5f;">// Getters and setters</span>
<span style="color: #808080;">80</span> <span style="color: #000000;">}</span>

Concrètement il s’agit d’ajouter insertable = false et updatable = false à la @JoinColumn ; et on n’aura pas besoin de setters pour les champs employe et concept dans EmployeConcept. C’est la classe id qui est responsable des insertions dans la base de données.

Faites attention à l’implémentation du constructeur de EmployeConcept.

<strong>Listing 4</strong>
<span style="color: #808080;">1</span> <span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">.employe = employe;</span>
<span style="color: #808080;">2</span> <span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">.concept = concept;</span>
<span style="color: #808080;">3</span> <span style="color: #ffffff;">        </span>
<span style="color: #808080;">4</span> <span style="color: #000000;">employe.getConcepts</span><span style="color: #000000;">()</span><span style="color: #000000;">.add</span><span style="color: #000000;">(</span><span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>
<span style="color: #808080;">5</span> <span style="color: #000000;">concept.getEmployes</span><span style="color: #000000;">()</span><span style="color: #000000;">.add</span><span style="color: #000000;">(</span><span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>

La partie de l’implémentation de ce constructeur montrée dans le Listing 4 assure l’intégrité référentielle entre les entités java en gérant la collection d’objets de chaque entité. Cependant elles ne permettent pas à elles seules de déclencher l’instruction SQL d’insertion du lien entre les deux entités, vu que les relations ManyToOne pour employe et concept sont en lecture seule.

Par contre l’instruction du Listing 5 :

<strong>Listing 5</strong>
<span style="color: #808080;">1</span> <span style="color: #7f0055;"><strong>this</strong></span><span style="color: #000000;">.id = </span><span style="color: #7f0055;"><strong>new </strong></span><span style="color: #000000;">EmployeConceptId</span><span style="color: #000000;">(</span><span style="color: #000000;">concept.getId</span><span style="color: #000000;">()</span><span style="color: #000000;">, employe.getId</span><span style="color: #000000;">())</span><span style="color: #000000;">;</span>

définit une fois pour toutes les valeurs de la clé primaire au moment de la construction d’une instance de EmployeConceptId. C’est cette ligne du constructeur de  EmployeConcept qui déclenche l’instruction d’insertion SQL dans la base de données.

Attention ! Une fois que les valeurs des champs de la classe id (EmployeConceptId) ont été définies grâce au constructeur d’EmployeConcept, elles ne seront jamais modifiées. En fait, elles forment la clé primaire de la table EMPLOYE_CONCEPT et elles dépendent des valeurs des clés primaires d’autres entités. D’où la notion d’id dérivé et l’absence de setters dans la classe id.

Le Listing 6, ci-dessous, montre comment créer et persister un lien entre un employé et un concept qui sont déjà persistés.

<strong>Listing 6</strong>
<span style="color: #808080;">1</span> <span style="color: #000000;">EmployeConcept lien = </span><span style="color: #7f0055;"><strong>new </strong></span><span style="color: #000000;">EmployeConcept</span><span style="color: #000000;">(</span><span style="color: #000000;">employe, concept, </span><span style="color: #990000;">5</span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>
<span style="color: #808080;">2</span> <span style="color: #000000;">entityManager.persist</span><span style="color: #000000;">(</span><span style="color: #000000;">lien</span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>

Le Listing 7, ci-après,  montre un code permettant de rompre ce lien

<strong>Listing 7</strong>
<span style="color: #808080;">1</span> <span style="color: #000000;">employe.getConcepts</span><span style="color: #000000;">()</span><span style="color: #000000;">.remove</span><span style="color: #000000;">(</span><span style="color: #000000;">lien</span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>
<span style="color: #808080;">2</span> <span style="color: #000000;">concept.getEmployes</span><span style="color: #000000;">()</span><span style="color: #000000;">.remove</span><span style="color: #000000;">(</span><span style="color: #000000;">lien</span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>
<span style="color: #808080;">3</span>
<span style="color: #808080;">4</span> <span style="color: #000000;">entityManager.remove</span><span style="color: #000000;">(</span><span style="color: #000000;">lien</span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>

Bien évidemment, il ne faut pas oublier de redéfinir les méthodes equals et hashCode dans EmployeConcept, en se basant sur le champ id de cette entité, afin d’assurer le bon fonctionnement des appels à la méthode remove sur les collections d’employés et de concepts. Attaention !  Ces appels ne garantissent que l’intégrité référentielle au niveau java. C’est l’appel à la méthode remove sur entityManager qui déclenchera réellement l’instruction delete en SQL.

Conclusion

En résumé, j’ai montré dans ce billet le mapping avec JPA 1.0, d’une table de jointure qui comporte des colonnes supplémentaires ne faisant pas partie des colonnes formant la clé primaire de cette table. Dans le prochain billet je montrerai une variante de cette solution en utilisant une annotation propre à JPA 2.0.

Categories: Java EE Tags: , , , , ,