Chapitre 19. Meilleures pratiques

Découpez finement vos classes et mappez les en utilisant <component>.

Utilisez une classe Addresse pour encapsuler Rue, Region, CodePostal. Ceci permet la réutilisation du code et simplifie la maintenance.

Déclarez des propriétés d'identifiants dans les classes persistantes.

Hibernate rend les propriétés d'identifiants optionnelles. Il existe beaucoup de raisons pour lesquelles vous devriez les utiliser. Nous recommandons que vous utilisiez des identifiants techniques (générés, et sans connotation métier) et de type non primitif. Pour un maximum de flexibilité, utilisez java.lang.Long ou java.lang.String.

Placez chaque mapping de classe dans son propre fichier.

N'utilisez pas un unique document de mapping. Mappez com.eg.Foo dans le fichier com/eg/Foo.hbm.xml. Cela prend tout son sens lors d'un travail en équipe.

Chargez les mappings comme des ressources.

Déployez les mappings en même temps que les classes qu'ils mappent.

Pensez à externaliser les chaînes de caractères.

Ceci est une bonne habitude si vos requêtes appellent des fonctions SQL qui ne sont pas au standard ANSI. Cette externalisation dans les fichiers de mapping rendra votre application plus portable.

Utilisez les variables "bindées".

Comme en JDBC, remplacez toujours les valeurs non constantes par "?". N'utilisez jamais la manipulation des chaînes de caractères pour remplacer des valeurs non constantes dans une requête ! Encore mieux, utilisez les paramètres nommés dans les requêtes.

Ne gérez pas vous mêmes les connexions JDBC.

Hibernate laisse l'application gérer les connexions JDBC. Vous ne devriez gérer vos connexions qu'en dernier recours. Si vous ne pouvez pas utiliser les systèmes de connexions livrés, réfléchissez à l'idée de fournir votre propre implémentation de net.sf.hibernate.connection.ConnectionProvider.

Pensez à utiliser les types utilisateurs.

Supposez que vous ayez une type Java, de telle bibliothèque, qui a besoin d'être persisté mais qui ne fournit pas les accesseurs nécessaires pour le mapper comme composant. Vous devriez implémenter net.sf.hibernate.UserType. Cette approche libère le code de l'application de l'implémentation des transformations vers / depuis les types Hibernate.

Utiliser du JDBC pur dans les goulets d'étranglement.

Dans certaines parties critiques de votre système d'un point de vue performance, quelques opérations (exemple : update et delete massifs) peuvent tirer partie d'un appel JDBC natif. Mais attendez de savoir que c'est un goulet d'étranglement. Ne supposez jamais qu'un appel JDBC sera forcément plus rapide. Si vous avez besoin d'utiliser JDBC directement, ouvrez une Session Hibernate et utilisez la connexion SQL sous-jacente. Ainsi vous pourrez utiliser la même stratégie de transation et la même gestion des connexions.

Comprendre le flush de Session.

De temps en temps la Session synchronise ses états persistants avec la base de données. Les performances seront affectées si ce processus arrive trop souvent. Vous pouvez parfois minimiser les flush non nécessaires en désactivant le flush automatique ou même en changeant l'ordre des opérations menées dans une transaction particulière.

Dans une architecture à trois couches, pensez à utiliser saveOrUpdate().

Quand vous utilisez une architecture à base de servlet / session bean, vous pourriez passer des objets chargés dans le bean session vers et depuis la couche servlet / jsp. Utilisez une nouvelle session pour traiter chaque requête. Utilisez Session.update() ou Session.saveOrUpdate() pour mettre à jour l'état persistant de votre objet.

Dans une architecture à deux couches, pensez à utiliser la déconnexion de session.

Les transactions de bases de données doivent être aussi courtes que possible pour une meilleure scalabilité. Cependant, il est souvent nécessaire d'implémenter de longues transactions applicatives, une simple unité de travail du point de vue de l'utilisateur. La transaction applicative peut s'étaler sur plusieurs cycles de requêtes/réponses du client. Utilisez soit les objets détachés ou, dans une architecture deux tiers, déconnectez simplement la session Hibernate de la connexion JDBC et reconnectez la à chaque requête suivante. N'utilisez jamais une seule session pour plus d'un cas d'utilisation de type transaction applicative, sinon vous vous retrouverez avec des données obsolètes.

Connsidérer que les exceptions ne sont pas rattrapables.

Il s'agit plus d'une pratique obligatoire que d'une "meilleure pratique". Quand une exception intervient, il faut faire un rollback de la Transaction et fermer la Session. Sinon, Hibernate ne peut garantir l'intégrité des états persistants en mémoire. En particulier, n'utilisez pas Session.load() pour déterminer si une instance avec un identifiant donné existe en base de données, utilisez find() (ou get()) à la place. Quelques exceptions sont récupérables, par exemple StaleObjectStateException et ObjectNotFoundException.

Préférez le chargement tardif des associations.

Utilisez le chargement complet (simple ou par jointure ouverte) avec modération. Utilisez les proxies et/ou les collections chargées tardivement pour la plupart des associations vers des classes qui ne sont pas en cache de niveau JVM. Pour les assocations de classes en cache, où il y a une forte probabilité que l'élément soit en cache, désactivez explicitement le chargement par jointures ouvertes en utilisant outer-join="false". Lorsqu'un chargement par jointure ouverte est approprié pour un cas d'utilisation particulier, utilisez une requête avec un left join fetch.

Pensez à abstraite votre logique métier d'Hibernate.

Cachez le mécanisme d'accès aux données (Hibernate) derrière une interface. Combinez les patterns DAO et Thread Local Session. Vous pouvez même avoir quelques classes persistées par du JDBC pur, associées à Hibernate via un UserType (ce conseil est valable pour des applications de taille respectables ; il n'est pas valable pour une application avec 10 tables).

Implémentez equals() et hashCode() en utilisant une clé métier.

Si vous comparez des objets en dehors de la session, vous devez implémenter equals() et hashCode(). A l'intérieur de la session, l'identité des objets java est assurée. Si vous implémentez ces méthodes, n'utilisez jamais les identifiants de la base de données ! Une instance transiante n'a pas de valeur d'identifiant et Hibernate en assignera une quand l'objet sera sauvé. Si l'objet est dans un Set quand il est en cours de sauvegarde, le hashcode changera donc, ce qui rompt le contrat. Pour implémenter equals() et hashCode(), utilisez une clé métier unique ce qui revient à comparer une combinaison de propriétés de classe. Souvenez vous que cette clé doit être stable et unique pendant la durée durant laquelle l'objet est dans un Set, et non pour tout son cycle de vie (pas aussi stable que la clé primaire de la base de données). ne comparez jamais des collections avec equals() (chargement tardif) et soyez prudents avec les autres classes dont vous pourriez n'avoir qu'un proxy.

N'utilisez pas d'associations de mapping exotiques.

De bons cas d'utilisation pour de vraies associations plusieurs-vers-plusieurs sont rares. La plupart du temps vous avez besoin d'informations additionnelles stockées dans la table d'association. Dans ce cas, il est préférable d'utiliser deux associations un-vers-plusieurs vers une classe de liaisons intermédiaire. En fait, nous pensons que la plupart des associations sont de type un-vers-plusieurs ou plusieurs-vers-un, vous devez être très attentifs lorsque vous utilisez autre chose et vous demander si c'est vraiment nécessaire.