Hibernate supporte des collections implémentant java.util.SortedMap
et java.util.SortedSet
. Vous devez spécifier un comparateur dans le fichier de mapping :
<set name="aliases" table="person_aliases" sort="natural"> <key column="person"/> <element column="name" type="string"/> </set> <map name="holidays" sort="my.custom.HolidayComparator"> <key column="year_id"/> <map-key column="hol_name" type="string"/> <element column="hol_date" type="date"/> </map>
Les valeurs permises pour l'attribut sort
sont unsorted
, natural
et le nom d'une classe implémentant java.util.Comparator
.
Les collections triées se comportent réellement comme java.util.TreeSet
ou java.util.TreeMap
.
Si vous voulez que la base de données elle-même ordonne les éléments de la collection, utilisez l'attribut order-by
des mappings set
, bag
ou map
. Cette solution est seulement disponible à partir du JDK 1.4 (c'est implémenté en utilisant LinkedHashSet
ou LinkedHashMap
). Ceci exécute le tri dans la requête SQL, pas en mémoire.
<set name="aliases" table="person_aliases" order-by="lower(name) asc"> <key column="person"/> <element column="name" type="string"/> </set> <map name="holidays" order-by="hol_date, hol_name"> <key column="year_id"/> <map-key column="hol_name" type="string"/> <element column="hol_date type="date"/> </map>
Notez que la valeur de l'attribut order-by
est un ordre SQL, pas un ordre HQL !
Les associations peuvent même être triées sur des critères arbitraires à l'exécution en utilisant un filter()
de collection.
sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();
A bidirectional association allows navigation from both "ends" of the association. Two kinds of bidirectional association are supported:
ensemble ou sac à une extrémité, une seule valeur à l'autre
ensemble ou sac aux deux extrémités
Vous pouvez spécifier une association plusieurs-vers-plusieurs bidirectionnelle simplement en mappant deux associations plusieurs-vers-plusieurs vers la même table de base de données et en déclarant une extrémité comme inverse (celle de votre choix, mais ça ne peut pas être une collection indexée).
Voici un exemple d'association bidirectionnelle plusieurs-vers-plusieurs ; chaque catégorie peut avoir plusieurs objets et chaque objet peut être dans plusieurs catégories :
<class name="Category"> <id name="id" column="CATEGORY_ID"/> ... <bag name="items" table="CATEGORY_ITEM"> <key column="CATEGORY_ID"/> <many-to-many class="Item" column="ITEM_ID"/> </bag> </class> <class name="Item"> <id name="id" column="ITEM_ID"/> ... <!-- inverse end --> <bag name="categories" table="CATEGORY_ITEM" inverse="true"> <key column="ITEM_ID"/> <many-to-many class="Category" column="CATEGORY_ID"/> </bag> </class>
Les changements faits uniquement sur l'extréminté inverse de l'association ne sont pas persistés. Ceci signifie qu'Hibernate a deux représentations en mémoire pour chaque association bidirectionnelles, un lien de A vers B et un autre de B vers A. C'est plus facile à comprendre si vous pensez au modèle objet de Java et comment nous créons une relation plusieurs-vers-plusieurs en Java :
category.getItems().add(item); // The category now "knows" about the relationship item.getCategories().add(category); // The item now "knows" about the relationship session.persist(item); // The relationship won't be saved! session.persist(category); // The relationship will be saved
La partie non-inverse est utilisée pour sauvegarder la représentation en mémoire dans la base de données.
Vous pouvez définir une association un-vers-plusieurs bidirectionnelle en mappant une association un-vers-plusieurs vers la(es)
même(s) colonne(s) de table qu'une association plusieurs-vers-un et en déclarant l'extrémité pluri-valuée inverse="true"
.
<class name="Parent"> <id name="id" column="parent_id"/> .... <set name="children" inverse="true"> <key column="parent_id"/> <one-to-many class="Child"/> </set> </class> <class name="Child"> <id name="id" column="child_id"/> .... <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/> </class>
Mapper une extrémité d'une association avec inverse="true"
n'affecte pas l'opération de cascades, ce sont des concepts orthogonaux !
Une association bidirectionnelle où une extrémité est représentée comme une <list>
ou une <map>
requiert une considération spéciale. Si il y a une propriété de la classe enfant qui mappe la colonne de l'index, pas de
problème, nous pouvons continuer à utiliser inverse="true"
sur le mapping de la collection :
<class name="Parent"> <id name="id" column="parent_id"/> .... <map name="children" inverse="true"> <key column="parent_id"/> <map-key column="name" type="string"/> <one-to-many class="Child"/> </map> </class> <class name="Child"> <id name="id" column="child_id"/> .... <property name="name" not-null="true"/> <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/> </class>
Mais, si il n'y a pas de telle prorpriété sur la classe enfant, nous ne pouvons pas penser à l'association comme vraiment
bidirectionnelle (il y a des informations disponibles à une extrémité de l'association qui ne sont pas disponibles à l'autre
extrémité). Dans ce cas, nous ne pouvons pas mapper la collection inverse="true"
. À la place, nous pourrions utiliser le mapping suivant :
<class name="Parent"> <id name="id" column="parent_id"/> .... <map name="children"> <key column="parent_id" not-null="true"/> <map-key column="name" type="string"/> <one-to-many class="Child"/> </map> </class> <class name="Child"> <id name="id" column="child_id"/> .... <many-to-one name="parent" class="Parent" column="parent_id" insert="false" update="false" not-null="true"/> </class>
Notez que dans ce mapping, l'extrémité de l'association contenant la collection est responsable des mises à jour de la clef étrangère. À faire : cela entraîne-t-il réellement des expressions updates inutiles ?
Il y a trois approches possibles pour mapper une association ternaire. L'une est d'utiliser une Map
avec une association en tant qu'index :
<map name="contracts"> <key column="employer_id" not-null="true"/> <map-key-many-to-many column="employee_id" class="Employee"/> <one-to-many class="Contract"/> </map>
<map name="connections"> <key column="incoming_node_id"/> <map-key-many-to-many column="outgoing_node_id" class="Node"/> <many-to-many column="connection_id" class="Connection"/> </map>
Une seconde approche est simplement de remodeler l'association comme une classe d'entité. C'est l'approche la plus commune.
Une alternative finale est d'utiliser des éléments composites, dont nous discuterons plus tard.
Si vous embrassez pleinement notre vue que les clefs composées sont une mauvaise chose et que des entités devraient avoir des identifiants artificiels (des clefs subrogées), alors vous pourriez trouver un peu curieux que les associations plusieurs-vers-plusieurs et les collections de valeurs que nous avons montré jusqu'ici mappent toutes des tables avec des clefs composées ! Maintenant, ce point est assez discutable ; une table d'association pure ne semble pas beaucoup bénéficier d'une clef subrogée (bien qu'une collection de valeur composées le pourrait). Néanmoins, Hibernate fournit une foncionnalité qui vous permet de mapper des associations plusieurs-vers-plusieurs et des collections de valeurs vers une table avec une clef subrogée.
L'élément <idbag>
vous laisse mapper une List
(ou une Collection
) avec une sémantique de sac.
<idbag name="lovers" table="LOVERS"> <collection-id column="ID" type="long"> <generator class="sequence"/> </collection-id> <key column="PERSON1"/> <many-to-many column="PERSON2" class="Person" fetch="join"/> </idbag>
Comme vous pouvez voir, un <idbag>
a un généréteur d'id artificiel, comme une classe d'entité ! Une clef subrogée différente est assignée à chaque ligne de
la collection. Cependant, Hibernate ne fournit pas de mécanisme pour découvrir la valeur d'une clef subrogée d'une ligne particulière.
Notez que les performances de la mise à jour d'un <idbag>
sont bien meilleures qu'un <bag>
ordinaire ! Hibernate peut localiser des lignes individuelles efficacement et les mettre à jour ou les effacer individuellement,
comme une liste, une map ou un ensemble.
Dans l'implémentation actuelle, la stratégie de la génération de l'identifiant native
n'est pas supportée pour les identifiants de collection <idbag>
.