Travailler avec des objets
Hibernate est une solution de mapping objet/relationnel complète qui ne masque pas
seulement au développpeur les détails du système de gestion de base de données sous-jacent,
mais offre aussi la gestion d'état des objets. C'est, contrairement
à la gestion de statements SQL dans les couches de persistance
habituelles JDBC/SQL, une vue orientée objet très naturelle de la persistance dans les
applications Java.
En d'autres mots, les développeurs d'applications Hibernate devrait toujours
réfléchir à l'état de leurs objets, et pas nécessairement à
l'exécution des expressions SQL. Cette part est prise en charge pas Hibernate et
seulement importante pour les développeurs d'applications lors du réglage de la
performance de leur système.
États des objets Hibernate
Hibernate définit et comprend les états suivants :
Éphémère (NdT : transient) - un objet est éphémère s'il a juste
été instancié en utilisant l'opérateur new. Il n'a aucune
représentation persistante dans la base de données et aucune valeur d'identifiant
n'a été assignée. Les instances éphémères seront détruites par le ramasse-miettes
si l'application n'en conserve aucune référence. Utilisez la Session
d'Hibernate pour rendre un objet persistant (et laisser Hibernate s'occuper des
expressions SQL qui ont besoin d'être exécutées pour cette transistion).
Persistant - une instance persistante a une représentation dans la
base de données et une valeur d'identifiant. Elle pourrait avoir juste été sauvegardée
ou chargée, pourtant, elle est par définition dans la portée d'une Session.
Hibernate détectera n'importe quels changements effectués sur un objet dans l'état
persistant et synchronisera l'état avec la base de données lors de la fin l'unité de travail.
Les développeurs n'exécutent pas d'expressions UPDATE ou
DELETE manuelles lorsqu'un objet devrait être rendu éphémère.
Détaché - une instance détachée est un objet qui a été persistant,
mais dont sa Session a été fermée. La référence à l'objet est
encore valide, bien sûr, et l'instance détachée pourrait même être modifiée dans cet
état. Une instance détachée peut être réattachée à une nouvelle Session
plus tard dans le temps, la rendant (et toutes les modifications avec) de nouveau persistante.
Cette fonctionnalité rend possible un modèle de programmation pour de longues unités de travail
qui requièrent un temps de réflexion de l'utilisateur. Nous les appelons des conversations,
c'est-à-dire une unité de travail du point de vue de l'utilisateur.
Nous alons maintenant dicuster des états et des transitions d'état (et des méthodes
d'Hibernate qui déclenchent une transition) plus en détails.
Rendre des objets persistants
Les instances nouvellement instanciées d'une classe persistante sont considérées
éphémères par Hibernate. Nous pouvons rendre une instance
éphémère persistante en l'associant avec une session :
Si Cat a un identifiant généré, l'identifiant est généré et assigné
au cat lorsque save() est appelée. Si Cat
a un identifiant assigned, ou une clef composée, l'identifiant
devrait être assigné à l'instance de cat avant d'appeler save().
Vous pouvez aussi utiliser persist() à la place desave(),
avec la sémantique définie plus tôt dans le brouillon d'EJB3.
Alternativement, vous pouvez assigner l'identifiant en utilisant une version
surchargée de save().
Si l'objet que vous rendez persistant a des objets associés (par exemple,
la collection kittens dans l'exemple précédent), ces objets
peuvent être rendus persistants dans n'importe quel ordre que vous souhaitez
à moins que vous ayez une contrainte NOT NULL sur la
colonne de la clef étrangère. Il n'y a jamais de risque de violer une
contrainte de clef étrangère. Cependant, vous pourriez violer une contrainte
NOT NULL si vous appeliez save() sur
les objets dans le mauvais ordre.
Habituellement, vous ne vous préoccupez pas de ce détail, puisque vous
utiliserez très probablement la fonctionnalité de persistance
transitive d'Hibernate pour sauvegarder les objets associés
automatiquement. Alors, même les violations de contrainte NOT NULL
n'ont plus lieu - Hibernate prendra soin de tout. La persistance transitive est
traitée plus loin dans ce chapitre.
Chargement d'un objet
Les méthodes load() de Session vous donnent
un moyen de récupérer une instance persistante si vous connaissez déjà son identifiant.
load() prend un objet de classe et chargera l'état dans une instance
nouvellement instanciée de cette classe, dans un état persistant.
Alternativement, vous pouvez charger un état dans une instance donnée :
Notez que load() lèvera une exception irrécupérable s'il
n'y a pas de ligne correspondante dans la base de données. Si la classe est mappée
avec un proxy, load() retourne juste un proxy non initialisé et
n'accède en fait pas à la base de données jusqu'à ce que vous invoquiez une
méthode du proxy. Ce comportement est très utile si vous souhaitez créer
une association vers un objet sans réellement le charger à partir de la base de
données. Cela permet aussi à de multiples instances d'être chargées comme un lot
si batch-size est défini pour le mapping de la classe.
Si vous n'êtes pas certain qu'une ligne correspondante existe, vous devriez
utiliser la méthode get(), laquelle accède à la base de
données immédiatement et retourne null s'il n'y a pas de ligne correspondante.
Vous pouvez même charger un objet en employant un SELECT ... FOR UPDATE SQL,
en utilisant un LockMode. Voir la documentation de l'API pour plus d'informations.
Notez que n'importe quelles instances associées ou collections contenues
ne sont pas sélectionnées par FOR UPDATE,
à moins que vous ne décidiez de spécifier lock ou all
en tant que style de cascade pour l'association.
Il est possible de re-charger un objet et toutes ses collections à n'importe quel moment,
en utilisant la méthode refresh(). C'est utile lorsque des "triggers" de
base de données sont utilisés pour initiliser certains propriétés de l'objet.
Une question importante apparaît généralement à ce point : combien (NdT : de données) Hibernate
charge-t-il de la base de données et combient de SELECTs utilisera-t-il ?
Cela dépent de la stratégie de récupération et cela est expliqué dans
.
Requêtage
Si vous ne connaissez par les identifiants des objets que vous recherchez, vous
avez besoin d'une requête. Hibernate supporte un langage de requêtes orientées objet
facile à utiliser mais puissant. Pour la création de requêtes par programmation,
Hibernate supporte une fonction de requêtage sophistiqué Criteria et Example (QBC et QBE).
Vous pouvez aussi exprimez votre requête dans le SQL natif de votre base de données,
avec un support optionnel d'Hibernate pour la conversion des ensembles de résultats en
objets.
Exécution de requêtes
Les requêtes HQL et SQL natives sont représentées avec une instance de org.hibernate.Query.
L'interface offre des méthodes pour la liaison des paramètres, la gestion des ensembles de resultats, et pour
l'exécution de la requête réelle. Vous obtenez toujours une Query en utilisant la
Session courante :
Une requête est généralement exécutée en invoquant list(),
le résultat de la requête sera chargée complètement dans une collection en mémoire.
Les intances d'entités recupérées par une requête sont dans un état persistant.
La méthode uniqueResult() offre un raccourci si vous
savez que votre requête retournera seulement un seul objet.
Itération de résultats
Occasionnellement, vous pourriez être capable d'obtenir de meilleures
performances en exécutant la requête avec la méthode iterate().
Ce sera généralement seulement le cas si vous espérez que les intances réelles
d'entité retournées par la requête soient déjà chargées dans la session ou le
cache de second niveau. Si elles ne sont pas cachées, iterate()
sera plus lent que list() et pourrait nécessiter plusieurs
accès à la base de données pour une simple requête, généralement 1
pour le select initial qui retourne seulement les identifiants, et n
selects supplémentaires pour initialiser les instances réelles.
Requêtes qui retournent des tuples
Les requêtes d'Hibernate retournent parfois des tuples d'objets, auquel cas chaque tuple
est retourné comme un tableau :
Résultats scalaires
Des requêtes peuvent spécifier une propriété d'une classe dans la clause select.
Elles peuvent même appeler des fonctions d'aggrégat SQL. Les propriétés ou les aggrégats sont
considérés comme des résultats "scalaires" (et pas des entités dans un état persistant).
Lier des paramètres
Des méthodes de Query sont fournies pour lier des
valeurs à des paramètres nommés ou à des paramètres de style JDBC ?.
Contrairement à JDBC, les numéros des paramètres d'Hibernate commencent à zéro.
Les paramètres nommés sont des identifiants de la forme :nom dans la chaîne de
caractères de la requête. Les avantages des paramètres nommés sont :
les paramètres nommés sont insensibles à l'ordre de leur place dans la chaîne
de la requête
ils peuvent apparaître plusieurs fois dans la même requête
ils sont auto-documentés
Itération "scrollable"
Si votre connecteur JDBC supporte les ResultSets "scrollables",
l'interface Query peut être utilisée pour obtenir un objet
ScrollableResults, lequel permet une navigation flexible dans les
résultats de la requête.
i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );
}
cats.close()]]>
Notez qu'une connexion ouverte (et un curseur) est requise pour cette fonctionnalité,
utilisez setMaxResult()/setFirstResult() si vous
avez besoin d'une fonctionnalité de pagination hors ligne.
Externaliser des requêtes nommées
Vous pouvez aussi définir des requêtes nommées dans le document de mapping.
(Souvenez-vous d'utiliser une section CDATA si votre requête
contient des caractères qui pourraient être interprétés comme des éléments XML.)
?
] ]>]]>
La liaison de paramètres et l'exécution sont fait par programmation :
Notez que le code réel du programme est indépendant du langage de requête qui est
utilisé, vous pouvez aussi définir des requêtes SQL nativez dans les méta-données, ou
migrer des requêtes existantes vers Hibernate en les plaçant dans les fichiers de mapping.
Notez aussi que la déclaration d'une requête dans un élément <hibernate-mapping>
nécessite un nom globalement unique pour la requête, alors que la déclaration d'une requête
dans une élément <class> est rendue unique de manière automatique par
la mise en préfixe du nom entièrement de la classe, par exemple
eg.Cat.ByNameAndMaximumWeight.
Filtrer des collections
Un filtre de collection est un type spécial de requête qui peut être
appliqué à une collection persistante ou à un tableau. La chaîne de requête peut se référer à
this, correspondant à l'élément de la collection courant.
La collection retournée est considérée comme un bag, et c'est une copie de la
collection donnée. La collection originale n'est pas modifiée (c'est contraire
à l'implication du nom "filtre"; mais cohérent avec le comportement attendu).
Observez que les filtres ne nécessitent pas une clause from (bien qu'ils
puissent en avoir une si besoin est). Les filtres ne sont pas limités à retourner des
éléments de la collection eux-mêmes.
Même une requête de filtre vide est utile, par exemple pour charger un sous-ensemble
d'éléments dans une énorme collection :
Requêtes Criteria
HQL est extrêmement puissant mais certains développeurs préfèrent construire des
requêtes dynamiquement, en utilisant l'API orientée objet, plutôt que construire
des chaînes de requêtes. Hibernate fournit une API intuitive de requête Criteria
pour ces cas :
Les APIs Criteria et Example associé sont
traitées plus en détail dans .
Requêtes en SQL natif
Vous pouvez exprimer une requête en SQL, en utilisant createSQLQuery()
et laisser Hibernate s'occuper du mapping des résultats vers des objets. Notez que vous
pouvez n'importe quand appeler session.connection() et utiliser
directement la Connection JDBC. Si vous choisissez d'utiliser
l'API Hibernate, vous devez mettre les alias SQL entre accolades :
Les requêtes SQL peuvent contenir des paramètres nommés et positionnels, comme des
requêtes Hibernate. Plus d'informations à propos des requêtes SQL natives dans Hibernate
peuvent être trouvées dans .
Modifier des objets persistants
Les instances persistantes transactionnelles (c'est-à-dire des objets
chargés, sauvegardés, créés ou requêtés par la Session) peuvent être
manipulées par l'application et n'importe quel changement vers l'état persistant sera
persisté lorsque la Session est "flushée" (traité
plus tard dans ce chapitre). Il n'y a pas besoin d'appeler une méthode particulière
(comme update(), qui a un but différent) pour rendre vos modifications
persistantes. Donc la manière la plus directe de mettre à jour l'état d'un objet est de
le charger avec load(), et puis le manipuler directement, tant que la
Session est ouverte :
Parfois ce modèle de programmation est inefficace puisqu'il nécessiterait un
SELECT SQL (pour charger l'objet) et un UPDATE
SQL (pour persister son état mis à jour) dans la même session. Aussi Hibernate offre
une autre approche, en utilisant des instances détachées.
Notez que Hibernate n'offre par sa propre API pour l'exécution directe
d'expressions UPDATE ou DELETE. Hibernate
est un service de gestion d'état, vous n'avez pas à penser
aux expressions pour l'utiliser. JDBC est une API parfaite
pour exécuter des expressions SQL, vous pouvez obtenir une Connection
JDBC n'importe quand en appelant session.connection(). En outre,
la notion d'opérations de masse entre en conflit avec le mapping objet/relationnel
pour les applications orientées processus de transactions en ligne. Les futures
versions d'Hibernate peuvent cependant fournir des fonctions d'opération de masse.
Voir pour les astuces possibles d'opérations groupées.
Modifier des objets détachés
Beaucoup d'applications ont besoin de récupérer un objet dans une transaction,
l'envoyer à la couche interfacée avec l'utilisateur pour les manipulations, puis
sauvegarder les changements dans une nouvelle transaction. Les applications
qui utilisent cette approche dans un environnement à haute concurrence utilisent
généralement des données versionnées pour assurer l'isolation pour les "longues"
unités de travail.
Hibernate supporte ce modèle en permettant pour le réattachement d'instances détachées
l'utilisation des méthodes Session.update() ou Session.merge() :
Si le Cat avec l'identifiant catId avait déjà
été chargé par secondSession lorsque l'application a essayé de le
réattacher, une exception aurait été levée.
Utilisez update() si vous êtes sure que la session ne contient pas
déjà une instance persistante avec le même identifiant, et merge()
si vous voulez fusionner vos modifications n'importe quand sans considérer l'état de
la session. En d'autres mots, update() est généralement la première méthode
que vous devriez appeler dans une session fraîche, pour s'assurer que le réattachement
de vos instances détachées est la première opération qui est exécutée.
L'application devrait individuellement update() (NdT : mettre à jour)
les instances détachées accessibles depuis l'instance détachée donnée si et
seulement si elle veut que leur état soit aussi mis à jour. Ceci
peut être automatisé bien sûr, en utilisant la persistance transitive,
voir .
La méthode lock() permet aussi à une application de réassocier un
objet avec une nouvelle session. Pourtant, l'instance détachée doit être non modifiée !
Notez que lock() peut être utilisé avec différents
LockModes, voir la documentation de l'API documentation et le chapitre
sur la gestion des transactions pour plus d'informations. Le réattachement n'est pas le seul
cas d'utilisation pour lock().
D'autres modèles pour de longues unités de travail sont traités dans .
Détection automatique d'un état
Les utilisateurs d'Hibernate ont demandé une méthode dont l'intention générale
serait soit de sauvegarder une instance éphémère en générant un nouvel identifiant,
soit mettre à jour/réattacher les instances détachées associées à l'identifiant courant.
La méthode saveOrUpdate() implémente cette fonctionnalité.
L'usage et la sémantique de saveOrUpdate() semble être confuse pour les
nouveaux utilisateurs. Premièrement, aussi longtemps que vous n'essayez pas d'utiliser des
instances d'une session dans une autre, vous ne devriez pas avoir besoin d'utiliser update(),
saveOrUpdate(), ou merge(). Certaines applications
n'utiliseront jamais ces méthodes.
Généralement update() ou saveOrUpdate() sont utilisées dans
le scénario suivant :
l'application charge un objet dans la première session
l'objet est passé à la couche utilisateur
certaines modifications sont effectuées sur l'objet
l'objet est retourné à la couche logique métier
l'application persiste ces modifications en appelant
update() dans une seconde sessin
saveOrUpdate() s'utilise dans le cas suivant :
si l'objet est déjà persistant dans cette session, ne rien faire
si un autre objet associé à la session a le même identifiant, lever une exception
si l'objet n'a pas de propriété d'identifiant, appeler save()
si l'identifiant de l'objet a une valeur assignée à un objet nouvellement instancié,
appeler save()
si l'objet est versionné (par <version> ou
<timestamp>), et la valeur de la propriété de version
est la même valeur que celle assignée à un objet nouvellement instancié, appeler
save()
sinon mettre à jour l'objet avec update()
et merge() est très différent :
s'il y a une instance persistante avec le même identifiant couramment
associée à la session, copier l'état de l'objet donné dans l'instance persistante
s'il n'y a pas d'instance persistante associée à cette session, essayer de le charger
à partir de la base de données, ou créer une nouvelle instance persistante
l'instance persistante est retournée
l'instance donnée ne devient pas associée à la session, elle reste détachée
Suppression d'objets persistants
Session.delete() supprimera l'état d'un objet de la base de données.
Bien sûr, votre application pourrait encore conserver une référence vers un objet effacé.
Il est mieux de penser à delete() comme rendant une instance persistante
éphémère.
Vous pouvez effacer des objets dans l'ordre que vous voulez, sans risque de violations
de contrainte de clef étrangère. Il est encore possible de violer une contrainte NOT
NULL sur une colonne de clef étrangère en effaçant des objets dans le
mauvais ordre, par exemple si vous effacer le parent, mais oubliez d'effacer les enfants.
Réplication d'objets entre deux entrepôts de données
Il est occasionnellement utile de pouvoir prendre un graphe d'instances persistantes
et de les rendre persistantes dans un entrepôt différent, sans regénérer les valeurs
des identifiants.
Le ReplicationMode détermine comment replicate()
traitera les conflits avec les lignes existantes dans la base de données.
ReplicationMode.IGNORE - ignore l'objet s'il y a une ligne
existante dans la base de données avec le même identifiant
ReplicationMode.OVERWRITE - écrase n'importe quelle ligne existante
dans la base de données avec le même identifiant
ReplicationMode.EXCEPTION - lève une exception s'il y une ligne dans
la base de données avec le même identifiant
ReplicationMode.LATEST_VERSION - écrase la ligne si son numéro de version
est plus petit que le numéro de version de l'objet, ou ignore l'objet sinon
Les cas d'utilisation de cette fonctionnalité incluent la réconciliation de données
entrées dans différentes base de données, l'extension des informations de configuration
du système durant une mise à jour du produit, retour en arrière sur les changements effectués
durant des transactions non-ACID, et plus.
Flush de la session
De temps en temps la Session exécutera les expressions SQL
requises pour syncrhoniser l'état de la connexion JDBC avec l'état des objets
retenus en mémoire. Ce processus, flush, arrive par défaut aux
points suivants :
lors de certaines exécutions de requête
lors d'un appel à org.hibernate.Transaction.commit()
lors d'un appel à Session.flush()
Les expressions SQL sont effectuées dans l'ordre suivant :
insertion des entités, dans le même ordre que celui des
objets correspondants sauvegardés par l'appel à Session.save()
mise à jours des entités
suppression des collections
suppression, mise à jour et insertion des éléments des collections
insertion des collections
suppression des entités, dans le même ordre que celui des objets
correspondants qui ont été supprimés par l'appel à Session.delete()
(Une exception est que des objets utilisant la génération native
d'identifiants sont insérés lorsqu'ils sont sauvegardés.)
Excepté lorsque vous appelez flush() explicitement, il n'y
absolument aucune garantie à propos de quand la Session
exécute les appels JDBC, seulement sur l'ordre dans lequel ils sont
exécutés. Cependant, Hibernate garantit que Query.list(..) ne
retournera jamais de données périmées, ni des données fausses.
Il est possible de changer le comportement par défaut, donc que le flush se produise
moins fréquemment. La classe FlushMode définit trois modes différents :
flush seulement lors du commit (et seulement quand l'API Transaction
d'Hibernate est utilisée), flush automatiquement en utilisant la procédure expliquée, ou
jamais de flush à moins que flush() soit appelée explicitement.
Le dernier mode est utile pour l'exécution de longues unités de travail, où une
Session est gardée ouverte et déconnectée pour un long moment
(voir ).
Durant le flush, une exception peut se produire (par exemple, si une opération de la
DML viole une contrainte). Puisque les exceptions de gestion impliquent une certaine
compréhension du comportement transactionnel d'Hibernate, nous le traitons dans
.
Persistance transitive
Il est assez pénible de sauvegarder, supprimer, ou réattacher des objets
un par un, surtout si vous traitez un graphe d'objets associés. Un cas habituel
est une relation parent/enfant. Considérez l'exemple suivant :
Si les enfants de la relation parent/enfant étaient des types de valeur (par exemple,
une collection d'adresses ou de chaînes de caractères), leur cycle de vie dépendraient
du parent et aucune action ne serait requise pour "cascader" facilement les
changements d'état. Si le parent est sauvegardé, les objets enfants de type de valeur sont
sauvegardés également, si le parent est supprimé, les enfants sont supprimés, etc. Ceci
fonctionne même pour des opérations telles que la suppression d'un enfant de la collection ;
Hibernate détectera cela et, puisque les objets de type de valeur ne peuvent pas avoir
des références partagées, supprimera l'enfant de la base de données.
Maintenant considérez le même scénario avec un parent et dont les objets enfants
sont des entités, et non des types de valeur (par exemple, des catégories et des
objets, ou un parent et des chatons). Les entités ont leur propre cycle de vie,
supportent les références partagées (donc supprimer une entité de la collection
ne signifie pas qu'elle peut être supprimée), et il n'y a par défaut pas de
cascade d'état d'une entité vers n'importe quelle entité associée. Hibernate
n'implémente pas la persistance par accessibilité par défaut.
Pour chaque opération basique de la session d'Hibernate - incluant persist(), merge(),
saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate() - il y a un
style de cascade correspondant. Respectivement, les styles de cascade s'appellent persist,
merge, save-update, delete, lock, refresh, evict, replicate. Si vous voulez qu'une
opération soit cascadée le long d'une association, vous devez l'indiquer dans le document de
mapping. Par exemple :
]]>
Les styles de cascade peuvent être combinés :
]]>
Vous pouvez même utiliser cascade="all" pour spécifier que toutes
les opérations devraient être cascadées le long de l'association. La valeur par défaut
cascade="none" spécifie qu'aucune opération ne sera cascadée.
Une style de cascade spécial, delete-orphan, s'applique seulement
aux associations un-vers-plusieurs, et indique que l'opération delete()
devrait être appliquée à n'importe quel enfant qui est supprimé de l'association.
Recommandations :
Cela n'a généralement aucun sens d'activer la cascade sur une association
<many-to-one> ou <many-to-many>. Les
cascades sont souvent utiles pour des associations
<one-to-one> et <one-to-many>.
Si la durée de vie de l'objet enfant est liée à la durée de vie de l'objet parent,
faites en un objet du cycle de vie en spécifiant
cascade="all,delete-orphan".
Sinon, vous pourriez ne pas avoir besoin de cascade du tout. Mais si vous pensez que vous
travaillerez souvent avec le parent et les enfants ensemble dans la même transaction, et
que vous voulez vous éviter quelques frappes, considérez l'utilisation de
cascade="persist,merge,save-update".
Mapper une association (soit une simple association valuée, soit une collection) avec
cascade="all" marque l'association comme une relation de style
parent/enfant où la sauvegarde/mise à jour/suppression du parent
entraîne la sauvegarde/mise à jour/suppression de l'enfant ou des enfants.
En outre, une simple référence à un enfant d'un parent persistant aura pour conséquence
la sauvegarde/mise à jour de l'enfant. Cette métaphore est cependant incomplète. Un enfant
qui devient non référencé par son parent n'est pas automatiquement
supprimée, excepté dans le cas d'une association <one-to-many>
mappée avec cascade="delete-orphan". La sémantique précise des opérations
de cascade pour une relation parent/enfant est la suivante :
Si un parent est passé à persist(), tous les enfant sont passés à
persist()
Si un parent est passé à merge(), tous les enfants sont passés à
merge()
Si un parent est passé à save(), update() ou
saveOrUpdate(), tous les enfants sont passés à saveOrUpdate()
Si un enfant détaché ou éphémère devient référencé par un parent persistant,
il est passé à saveOrUpdate()
Si un parent est supprimé, tous les enfants sont passés à delete()
Si un enfant est déréférencé par un parent persistant, rien de spécial
n'arrive - l'application devrait explicitement supprimer l'enfant si nécessaire -
à moins que cascade="delete-orphan" soit paramétré,
au quel cas l'enfant "orphelin" est supprimé.
Enfin, la cascade des opérations peut être effectuée sur un graphe donné lors
de l'appel de l'opération or lors du flush
suivant. Toutes les opérations, lorsque cascadées, le sont sur toutes les entités
associées atteignables lorsque l'opétation est exécutée. Cependant
save-upate et delete-orphan sont cascadées
à toutes les entités associées atteignables lors du flush de la
Session.
Utilisation des méta-données
Hibernate requiert un modèle de méta-niveau très riche de toutes les entités et types valués.
De temps en temps, ce modèle est très utile à l'application elle même. Par exemple,
l'application pourrait utiliser les méta-données d'Hibernate pour implémenter un algorithme
de copie en profondeur "intelligent" qui comprendrait quels objets devraient copiés
(par exemple les types de valeur mutables) et lesquels ne devraient pas l'être (par exemple
les types de valeurs immutables et, possiblement, les entités associées).
Hibernate expose les méta-données via les interfaces ClassMetadata
et CollectionMetadata et la hiérarchie Type.
Les instances des interfaces de méta-données peuvent être obtenues à partir de la
SessionFactory.