Chapitre 2. Architecture

2.1. Généralités

Voici une vue (très) haut niveau de l'architecture d'Hibernate :

Ce diagramme montre Hibernate utilisant une base de données et des données de configuration pour fournir un service de persistance (et des objets persistants) à l'application.

Nous aimerions décrire une vue plus détaillée de l'architecture. Malheureusement, Hibernate est flexible et supporte différentes approches. Nous allons en montrer les deux extrêmes. L'architecture légère laisse l'application fournir ses propres connexions JDBC et gérer ses propres transactions. Cette approche utilise le minimum des APIs Hibernate :

L'architecture la plus complète abstrait l'application des APIs JDBC/JTA sous-jacentes et laisse Hibernate s'occuper des détails.

Voici quelques définitions des objets des diagrammes :

SessionFactory (org.hibernate.SessionFactory)

Un cache threadsafe (immuable) des mappings vers une (et une seule) base de données. Une factory (fabrique) de Session et un client de ConnectionProvider. Peut contenir un cache optionnel de données (de second niveau) qui est réutilisable entre les différentes transactions que cela soit au sein du même processus (JVLM) ou par plusieurs n½uds d'un cluster.

Session (org.hibernate.Session)

Un objet mono-threadé, à durée de vie courte, qui représente une conversation entre l'application et l'entrepôt de persistance. Encapsule une connexion JDBC. Factory (fabrique) des objets Transaction. Contient un cache (de premier niveau) des objets persistants, ce cache est obligatoire. Il est utilisé lors de la navigation dans le graphe d'objets ou lors de la récupération d'objets par leur identifiant.

Objets et Collections persistants

Objets mono-threadés à vie courte contenant l'état de persistance et la fonction métier. Ceux-ci sont en général les objets de type JavaBean (ou POJOs) ; la seule particularité est qu'ils sont associés avec une (et une seule) Session. Dès que la Session est fermée, ils seront détachés et libres d'être utilisés par n'importe laquelle des couches de l'application (ie. de et vers la présentation en tant que Data Transfer Objects - DTO : objet de transfert de données).

Objets et collections transients

Instances de classes persistantes qui ne sont actuellement pas associées à une Session. Elles ont pu être instanciées par l'application et ne pas avoir (encore) été persistées ou elle ont pu être instanciées par une Session fermée.

Transaction (org.hibernate.Transaction)

(Optionnel) Un objet mono-threadé à vie courte utilisé par l'application pour définir une unité de travail atomique. Abstrait l'application des transactions sous-jacentes qu'elles soient JDBC, JTA ou CORBA. Une Session peut fournir plusieurs Transactions dans certains cas. Toutefois, la délimitation des transactions, via l'API d'Hibernate ou par la Transaction sous-jacente, n'est jamais optionnelle!

ConnectionProvider (org.hibernate.connection.ConnectionProvider)

(Optionnel) Une fabrique de (pool de) connexions JDBC. Abstrait l'application de la Datasource ou du DriverManager sous-jacent. Non exposé à l'application, mais peut être étendu/implémenté par le développeur.

TransactionFactory (org.hibernate.TransactionFactory)

(Optionnel) Une fabrique d'instances de Transaction. Non exposé à l'application, mais peut être étendu/implémenté par le développeur.

Interfaces d'extension

Hibernate fournit de nombreuses interfaces d'extensions optionnelles que vous pouvez implémenter pour personnaliser le comportement de votre couche de persistance. Reportez vous à la documentation de l'API pour plus de détails.

Dans une architecture légère, l'application n'aura pas à utiliser les APIs Transaction/TransactionFactory et/ou n'utilisera pas les APIs ConnectionProvider pour utiliser JTA ou JDBC.

2.2. Etats des instances

Une instance d'une classe persistante peut être dans l'un des trois états suivants, définis par rapport à un contexte de persistance. L'objet Session d'hibernate correspond à ce concept de contexte de persistance :

passager (transient)

L'instance n'est pas et n'a jamais été associée à un contexte de persistance. Elle ne possède pas d'identité persistante (valeur de clé primaire)

persistant

L'instance est associée au contexte de persistance. Elle possède une identité persistante (valeur de clé primaire) et, peut-être, un enregistrement correspondant dans la base. Pour un contexte de persistance particulier, Hibernate garantit que l'identité persistante est équivalente à l'identité Java (emplacement mémoire de l'objet)

détaché

L'instance a été associée au contexte de persistance mais ce contexte a été fermé, ou l'instance a été sérialisée vers un autre processus. Elle possède une identité persistante et peut-être un enregistrement correspondant dans la base. Pour des instances détachées, Hibernate ne donne aucune garantie sur la relation entre l'identité persistante et l'identité Java.

2.3. Intégration JMX

JMX est le standard J2EE de gestion des composants Java. Hibernate peut être géré via un service JMX standard. Nous fournissons une implémentation d'un MBean dans la distribution : org.hibernate.jmx.HibernateService.

Pour avoir un exemple sur la manière de déployer Hibernate en tant que service JMX dans le serveur d'application JBoss Application Server, référez vous au guide utilisateur JBoss (JBoss User Guide). Si vous déployez Hibernate via JMX sur JBoss AS, vous aurez également les bénéfices suivants :

  • Gestion de la session : Le cycle de vie de la Session Hibernate peut être automatiquement limitée à la portée d'une transaction JTA. Cela signifie que vous n'avez plus besoin d'ouvrir et de fermer la Session manuellement, cela devient le travail de l'intercepteur EJB de JBoss. Vous n'avez pas non plus à vous occuper des démarcations des transactions dans votre code (sauf si vous voulez écrire une couche de persistance qui soit portable, dans ce cas vous pouvez utiliser l'API optionnelle Transaction d'Hibernate). Vous appelez l'HibernateContext pour accéder à la Session.

  • Déploiement HAR : Habituellement vous déployez le service JMX Hibernate en utilisant le descripteur de déploiement de JBoss (dans un fichier EAR et/ou un SAR), il supporte toutes les options de configuration usuelles d'une SessionFactory Hibernate. Cependant, vous devez toujours nommer tous vos fichiers de mapping dans le descripteur de déploiement. Si vous décidez d'utiliser le déploiement optionnel sous forme de HAR, JBoss détectera automatiquement tous vos fichiers de mapping dans votre fichier HAR.

Consultez le guide d'utilisation de JBoss AS pour plus d'informations sur ces options.

Les statistiques pendant l'exécution d'Hibernate (au runtime) sont une autre fonctionnalité disponible en tant que service JMX. Voyez pour cela Section 3.4.6, « Statistiques Hibernate ».

2.4. Support JCA

Hibernate peut aussi être configuré en tant que connecteur JCA. Référez-vous au site web pour de plus amples détails. Il est important de noter que le support JCA d'Hibernate est encore considéré comme expérimental.

2.5. Sessions Contextuelles

Certaines applications utilisant Hibernate ont besoin d'une sorte de session "contextuelle", où une session est liée à la portée d'un contexte particulier. Cependant, les applications ne définissent pas toutes la notion de contexte de la même manière, et différents contextes définissent différentes portées à la notion de "courant". Les applications à base d'Hibernate, versions précédentes à la 3.0 utilisaient généralement un principe maison de sessions contextuelles basées sur le ThreadLocal, ainsi que sur des classes utilitaires comme HibernateUtil, ou utilisaient des framework tiers (comme Spring ou Pico) qui fournissaient des sessions contextuelles basées sur l'utilisation de proxy/interception.

A partir de la version 3.0.1, Hibernate a ajouté la méthode SessionFactory.getCurrentSession(). Initialement, cela demandait l'usage de transactions JTA, où la transaction JTA définissait la portée et le contexte de la session courante. L'équipe Hibernate pense que, étant donnée la maturité des implémentations de JTA TransactionManager , la plupart (sinon toutes) des applications devraient utiliser la gestion des transactions par JTA qu'elles soient ou non déployées dans un conteneur J2EE. Par conséquent, vous devriez toujours contextualiser vos sessions, si vous en avez besoin, via la méthode basée sur JTA.

Cependant, depuis la version 3.1, la logique derrière SessionFactory.getCurrentSession() est désormais branchable. A cette fin, une nouvelle interface d'extension (org.hibernate.context.CurrentSessionContext) et un nouveau paramètre de configuration (hibernate.current_session_context_class) ont été ajoutés pour permettre de configurer d'autres moyens de définir la portée et le contexte des sessions courantes.

Allez voir les Javadocs de l'interface org.hibernate.context.CurrentSessionContext pour une description détaillée de son contrat. Elle définit une seule méthode, currentSession(), depuis laquelle l'implémentation est responsable de traquer la session courante du contexte. Hibernate fournit deux implémentation de cette interface.

  • org.hibernate.context.JTASessionContext - les sessions courantes sont associées à une transaction JTA. La logique est la même que l'ancienne approche basée sur JTA. Voir les javadocs pour les détails.

  • org.hibernate.context.ThreadLocalSessionContext - les sessions courantes sont associées au thread d'exécution. Voir les javadocs pour les détails.

  • org.hibernate.context.ManagedSessionContext - les sessions courantes sont traquées par l'exécution du thread. Toutefois, vous êtes responsable de lier et délier une instance de Session avec les méthodes statiques de cette classes, qui n'ouvre, ne flush ou ne ferme jamais de Session.

Les deux implémentations fournissent un modèle de programmation de type "une session - une transaction à la base de données", aussi connu sous le nom de session-per-request. Le début et la fin d'une session Hibernate sont définis par la durée d'une transaction de base de données. Si vous utilisez une démarcation programmatique de la transaction (par exemple sous J2SE ou JTA/UserTransaction/BMT), nous vous conseillons d'utiliser l'API Hibernate Transaction pour masquer le système de transaction utilisé. Si vous exécutez sous un conteneur EJB qui supporte CMT, vous n'avez besoin d'aucune opérations de démarcations de session ou transaction dans votre code puisque tout est géré de manière déclarative. Référez vous à Chapitre 11, Transactions et accès concurrents pour plus d'informations et des exemples de code.

Le paramètre de configuration hibernate.current_session_context_class définit quelle implémentation de org.hibernate.context.CurrentSessionContext doit être utilisée. Notez que pour assurer la compatibilité avec les versions précédentes, si ce paramètre n'est pas défini mais qu'un org.hibernate.transaction.TransactionManagerLookup est configuré, Hibernate utilisera le org.hibernate.context.JTASessionContext. La valeur de ce paramètre devrait juste nommer la classe d'implémentation à utiliser, pour les deux implémentations fournies, il y a cependant deux alias correspondant: "jta" et "thread".