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 Pagination Si vous avez besoin de spécifier des liens sur votre ensemble de résultats (le nombre maximum de lignes que vous voulez récupérez et/ou la première ligne que vous voulez récupérer) vous devriez utiliser des méthodes de l'interface Query : Hibernate sait comment traduite cette requête de limite en SQL natif pour votre SGBD. 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.