Hibernate
Architecture
figure 1.0 |
L'architecture de Hibernate est faite de telle sorte que l'on puisse l'intégrer facilement à d'autre APIs sans qu'on soit obligé de le connaître en détail.
Hibernate est composé de plusieurs classes et interfaces et les développeurs ne se servent que d'une petite partie.
Lorsqu'on travaille avec l'ORM Hibernate, le chargement d'un objet mappé depuis une base de données ou vers une base de données n'est possible que si on a une session ouverte et active.
L'objet Session offre l'interface principale permettant de travailler avec des bases de données. Ceci revient à dire que des objets persistants sont sauvegardés dans la base de données ou retirés de cette dernière en se servant d'un objet Session.
Il est très facile de créer un objet Session ; outre le faite qu'il permet de faire la connexion à la base de données, en s'en servant, on a automatiquement le maintien d'une cache durant la durée de vie du thread au sein duquel elle est ouverte par l'application (une requête).
Typiquement, une session est créée pour gérer une seule "requête" au sein d'une application mais il arrive qu'on s'en sert pour gérer plusieurs requêtes; dans tous les cas un objet Session ne doit pas rester ouvert longtemps.
Pour rendre une session active, il faut tout simplement la déclarer comme suit:
org.hibernate.Session session = sessionFactory.openSession ();
Pour modifier la BD, on se sert d'un objet Transaction via un objet Session; Toute transaction Hibernate d'avec JDBC, JTA ou CORBA est gérée par un TransactionManager. Comme l'objet Session, un objet Transaction ne doit pas rester ouvert trop longtemps si c'est possible.
Pour faire des requêtes sur la BD avec Hibernate, on se sert des objets Query et Criteria.
Composants
Les principaux composants permettant d'obtenir une session Hibernate sont:
- SessionFactory (org.hibernate.SessionFactory)
Cette classe doit être créée assez tôt dans l'application car c'est elle qui constitue un cache threadsafe (immuable) des mappings vers une (et une seule) base de données. C'est une fabrique (factory) de Session et c'est lui le client de ConnectionProvider (interface utilisée par le système) pour obtenir une connection à la BD utilisée. Un objet SessionFactory 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 noeuds d'un cluster. - Session (org.hibernate.Session)
L'objet Session est, mono-threadé, à durée de vie courte; il représente une conversation entre l'application et l'entrepôt de persistance. Il encapsule une connexion JDBC; C'est une fabrique (Factory) des objets Transaction; il contient un cache (de premier niveau) des objets persistants; ce cache est obligatoire et est utilisé lors de la navigation dans le graphe d'objets ou lors de la récupération d'objets par leur identifiant. - Transaction (org.hibernate.Transaction)
L'utilisation de cette classe est Optionnelle; L'objet de type Transaction est mono-threadé, à vie courte et est utilisé par l'application pour définir une unité de travail atomique. 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)
L'utilisation de cette fabrique de (pool de) connexions JDBC est Optionnelle. Cet objet n'est pas exposé à l'application, mais peut être étendu/implémenté par le développeur. - TransactionFactory (org.hibernate.TransactionFactory)
Son utilisation est Optionnelle.C'est une fabrique d'instances de Transaction non exposée à l'application, mais qui peut être étendue/implémentée par le développeur.
Les trois étapes suivantes permettent de démarrer une application Hibernate:
On commence par une Instanciation de l'objet Configuration ....,
puis on réalise une Construction de la sessionFactory......
et on fait une Demande de la nouvelle session à la SessionFactory ....
(Tout ceci sera illustré par des exemples)........
Les classes métiers persistantes
Les classes persistantes d'une application sont les classes qui implémentent les entités d'un problème métier (ex. Client et Commande dans une application de commerce électronique). Toutes les instances d'une classe persistante ne sont pas forcément dans l'état persistant - une instance peut être éphémère (NdT : transient) ou détachée.
Hibernate fonctionne de manière optimale lorsque ces classes suivent quelques règles simples, aussi connues comme le modèle de programmation Plain Old Java Object (POJO). Cependant, aucune de ces règles ne sont des besoins absolus. En effet, Hibernate3 suppose très peu de choses à propos de la nature de vos objets persistants. Vous pouvez exprimer un modèle de domaine par d'autres moyens : utiliser des arbres d'instances de Map, par exemple.
Implémentation de equals() et de hashCode()
On doit surcharger les méthodes equals() et hashCode() si on a l'intention de mettre des instances de classes persistantes dans un Set (ceci est la manière recommandée pour représenter des associations pluri-valuées) et ou d'utiliser le ré-attachement d'instances détachées.
Hibernate garantit l'équivalence de l'identité de la classe persistante (ligne dans une base de données) et l'identité de la classe Java seulement à l'intérieur de la portée d'une session particulière. Donc dès que nous mélangeons des instances venant de différentes sessions, nous devons implémenter equals() et hashCode() si nous souhaitons avoir une sémantique correcte pour les Sets.
La manière la plus évidente est d'implémenter equals()/hashCode() en comparant la valeur de l'identifiant des deux objets. Si cette valeur est identique, les deux doivent représenter la même ligne dans la base de de données; ,dans ce cas ils sont donc égaux (si les deux sont ajoutés à un Set, nous n'aurons qu'un seul élément dans le Set). Malheureusement, nous ne pouvons pas utiliser cette approche avec des identifiants générés ! Hibernate n'assignera de valeur d'identifiant qu'aux objets qui sont persistants, une instance nouvellement créée n'aura donc pas de valeur d'identifiant ! De plus, si une instance est non sauvegardée et actuellement dans un Set, le sauvegarder assignera une valeur d'identifiant à l'objet. Si equals() et hashCode() sont basées sur la valeur de l'identifiant, le code de hachage devrait changer, rompant le contrat du Set. Notez que ceci n'est pas un problème d'Hibernate, mais la sémantique normale de Java pour l'identité d'un objet et l'égalité. On implémente equals() et hashCode() en utilisant l'égalité par clé métier. L'égalité par clé métier signifie que la méthode equals() compare uniquement les propriétés qui forment une clé métier, une clé qui identifierait notre instance dans le monde réel (une clé candidate naturelle).
(à suivre)..