Classes persistantes Les classes persistantes sont les classes d'une application 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 - au lieu de cela, 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. Un exemple simple de POJO Toute bonne application Java nécessite une classe persistante représentant les félins. Il y a quatre règles à suivre ici : Implémenter un constructeur sans argument Cat a un constructeur sans argument. Toutes les classes persistantes doivent avoir un constructeur par défaut (lequel peut ne pas être public) pour qu'Hibernate puissent les instancier en utilisant Constructor.newInstance(). Nous recommandons fortement d'avoir un constructeur par défaut avec au moins une visibilité paquet pour la génération du proxy à l'exécution dans Hibernate. Fournir une propriété d'indentifiant (optionnel) Cat possède une propriété appelée id. Cette propriété mappe la valeur de la colonne de clé primaire de la table d'une base de données.La propriété aurait pu s'appeler complètement autrement, et son type aurait pu être n'importe quel type primitif, n'importe quel "encapsuleur" de type primitif, java.lang.String ou java.util.Date. (Si votre base de données héritée possède des clés composites, elles peuvent être mappées en utilisant une classe définie par l'utilisateur et possédant les propriétés associées aux types de la clé composite - voir la section concernant les identifiants composites plus tard). La propriété d'identifiant est strictement optionnelle. Vous pouver l'oublier et laisser Hibernate s'occuper des identifiants de l'objet en interne. Toutefois, nous ne le recommandons pas. En fait, quelques fonctionnalités ne sont disponibles que pour les classes déclarant un identifiant de propriété : Les réattachements transitifs pour les objets détachés (mise à jour en cascade ou fusion en cascade) - voir Session.saveOrUpdate() Session.merge() Nous recommandons que vous déclariez les propriétés d'identifiant de manière uniforme. Nous recommandons également que vous utilisiez un type nullable (ie. non primitif). Favoriser les classes non finales (optionnel) Une fonctionnalité clef d'Hibernate, les proxies, nécessitent que la classe persistente soit non finale ou qu'elle soit l'implémentation d'une interface qui déclare toutes les méthodes publiques. Vous pouvez persister, grâce à Hibernate, les classes final qui n'implémentent pas d'interface, mais vous ne pourrez pas utiliser les proxies pour les chargements d'associations paresseuses - ce qui limitera vos possibilités d'ajustement des performances. Vous devriez aussi éviter de déclarer des méthodes public final sur des classes non-finales. Si vous voulez utiliser une classe avec une méthode public final, vous devez explicitement désactiver les proxies en paramétrant lazy="false". Déclarer les accesseurs et mutateurs des attributs persistants (optionnel) Cat déclare des mutateurs pour toutes ses champs persistants. Beaucoup d'autres solutions de mapping Objet/relationnel persistent directement les variables d'instance. Nous pensons qu'il est bien mieux de fournir une indirection entre le schéma relationnel et les structures de données internes de la classe. Par défaut, Hibernate persiste les propriétés suivant le style JavaBean, et reconnaît les noms de méthodes de la forme getFoo, isFoo et setFoo. Nous pouvons changer pour un accès direct aux champs pour des propriétés particulières, si besoin est. Les propriétés n'ont pas à être déclarées publiques - Hibernate peut persister une propriété avec un paire de getter/setter de visibilité par défault, protected ou private. Implémenter l'héritage Une sous-classe doit également suivre la première et la seconde règle. Elle hérite sa propriété d'identifiant de Cat. Implémenter <literal>equals()</literal> et <literal>hashCode()</literal> Vous devez surcharger les méthodes equals() et hashCode() si vous avez l'intention de mettre des instances de classes persistantes dans un Set (la manière recommandée pour représenter des associations pluri-valuées) et avez l'intention d'utiliser le réattachement d'instances détachées Hibernate garantit l'équivalence de l'identité persistante (ligne de base de données) et l'identité 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 de base de données, 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. Regardez sur le site web d'Hibernate pour une discussion complète de ce problème. 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é. Nous recommandons donc d'implémenter 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) : Notez qu'une clef métier ne doit pas être solide comme une clef primaire de base de données (voir ). Les propriétés immuables ou uniques sont généralement de bonnes candidates pour une clef métier. Modèles dynamiques Notez que la fonctionnalités suivantes sont actuellement considérées comme expérimentales et peuvent changer dans un futur proche. Les entités persistantes ne doivent pas nécessairement être représentées comme des classes POJO ou des objets JavaBean à l'exécution. Hibernate supporte aussi les modèles dynamiques (en utilisant des Maps de Maps à l'exécution) et la représentation des entités comme des arbres DOM4J. Avec cette approche, vous n'écrivez pas de classes persistantes, seulement des fichiers de mapping. Par défaut, Hibernate fonctionne en mode POJO normal. Vous pouvez paramétrer un mode de représentation d'entité par défaut pour une SessionFactory particulière en utilisant l'option de configuration default_entity_mode (voir ). Les exemples suivants démontrent la représentation utilisant des Maps. D'abord, dans le fichier de mapping, un entity-name doit être déclaré au lieu (ou en plus) d'un nom de classe : ]]> Notez que même si des associations sont déclarées en utilisant des noms de classe cible, le type de cible d'une association peut aussi être une entité dynamique au lieu d'un POJO. Après avoir configuré le mode d'entité par défaut à dynamic-map pour la SessionFactory, nous pouvons lors de l'exécution fonctionner avec des Maps de Maps : Les avantages d'un mapping dynamique sont un gain de temps pour le prototypage sans la nécessité d'implémenter les classes d'entité. Pourtant, vous perdez la vérification du typage au moment de la compilation et aurez plus d'exceptions à gérer lors de l'exécution. Grâce au mapping d'Hibernate, le schéma de la base de données peut facilement être normalisé et solidifié, permettant de rajouter une implémentation propre du modèle de domaine plus tard. Les modes de représentation d'une entité peut aussi être configuré par Session : Veuillez noter que l'appel à getSession() en utilisant un EntityMode se fait sur l'API Session, pas SessionFactory. De cette manière, la nouvelle Session partage les connexions JDBC, transactions et autres informations de contexte sous-jacentes. Cela signifie que vous n'avez pas à appeler flush() et close() sur la Session secondaire, et laissez aussi la gestion de la transaction et de la connexion à l'unité de travail primaire. Plus d'informations à propos de la représentation XML peuvent être trouvées dans . Tuplizers org.hibernate.tuple.Tuplizer, et ses sous-interfaces, sont responsables de la gestion d'une représentation particulière d'un morceau de données, en fonction du org.hibernate.EntityMode de réprésentation. Si un morceau donné de données est pensé comme une structure de données, alors un tuplizer est la chose qui sait comment créer une telle structure de données, comment extraire des valeurs et injecter des valeurs dans une telle structure de données. Par exemple, pour le mode d'entité POJO, le tuplizer correspondant sait comment créer le POJO à travers son constructeur et comment accéder aux propriétés du POJO utilisant les accesseurs de la propriété définie. Il y a deux types de Tuplizers haut niveau, représentés par les interfaces org.hibernate.tuple.EntityTuplizer et org.hibernate.tuple.ComponentTuplizer. Les EntityTuplizers sont responsables de la gestion des contrats mentionnés ci-dessus pour les entités, alors que les ComponentTuplizers s'occupent des composants. Les utilisateurs peuvent aussi brancher leurs propres tuplizers. Peut-être vous est-il nécessaire qu'une implémentation de java.util.Map autre que java.util.HashMap soit utilisée dans le mode d'entité dynamic-map ; ou peut-être avez-vous besoin de définir une statégie de génération de proxy différente de celle utilisée par défaut. Les deux devraient être effectuées en définissant une implémentation de tuplizer utilisateur. Les définitions de tuplizers sont attachées au mapping de l'entité ou du composant qu'ils sont censés gérer. Retour à l'exemple de notre entité utilisateur : ... public class CustomMapTuplizerImpl extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer { // override the buildInstantiator() method to plug in our custom map... protected final Instantiator buildInstantiator( org.hibernate.mapping.PersistentClass mappingInfo) { return new CustomMapInstantiator( mappingInfo ); } private static final class CustomMapInstantiator extends org.hibernate.tuple.DynamicMapInstantitor { // override the generateMap() method to return our custom map... protected final Map generateMap() { return new CustomMap(); } } }]]> TODO: Document user-extension framework in the property and proxy packages