Détection automatique des classes persistantes avec Hibernate

18 mars 2008 par Thierry Chatel


Une fonctionnalité bien pratique de JPA (Java Persistence API), la partie persistance de la norme EJB 3, est la détection automatique des classes persistantes. Les implémentations de JPA répèrent automatiquement les classes de l’application signalées par les annotations adéquates.

Quand on utilise directement Hibernate, sans passer par l’EntityManager supportant la norme JPA, il n’y a aucune détection automatique, ce qui oblige à inscrire manuellement chaque nouvelle classe persistante, et l’on y pense souvent en se rendant compte que l’application ne fonctionne plus parce qu’on a oublié ce détail fastidieux.

Mais il est possible d’ajouter facilement à Hibernate ce mécanisme si pratique, surtout pour un projet en cours de développement.

L’extension décrite ici se base sur l’article Scanning Java Annotations at Runtime de Bill Burke, et l’outil Scannotation du même auteur qui permet de retrouver très facilement les classes Java d’une application par rapport aux annotations qu’elles portent.

Les lignes suivantes permettent de constituer une base de données des classes annotées, en scannant les classes compilées (ici celles trouvées dans la ressource contenant le fichier de configuration d’Hibernate) :

URL[] urls = ClasspathUrlFinder.findResourceBases("hibernate.cfg.xml");
AnnotationDB db = new AnnotationDB();
db.scanArchives(urls);

Il suffit ensuite d’utiliser la méthode suivante pour récupérer la liste des classes annotés par javax.persistence.Entity.class :

db.getAnnotationIndex().get(javax.persistence.Entity.class.getName());

Si l’on utilise avec Hibernate les annotations définies dans JPA, il faut rechercher les classes annotés par :

  • javax.persistence.Entity
  • javax.persistence.Embeddable
  • javax.persistence.Embeddable

Dans le cas d’une utilisation d’Hibernate avec JBoss Seam, le plus simple est de surcharger le composant Seam persistence:hibernate-session-factory, pour qu’il gère cette détection automatique des classes persistantes, comme ci-dessous :

package fr.iocean.util.persistence;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.SessionFactory;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Startup;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.persistence.HibernateSessionFactory;
import org.scannotation.AnnotationDB;
import org.scannotation.ClasspathUrlFinder;

/**
* Surcharge le composant Seam "hibernateSessionFactory" pour détecter automatiquement
* les entités persistantes. <br/>
* Recherche et charge les classes annotées avec les entités suivantes : <br/>
* <ul>
*   <li>javax.persistence.Entity</li>
*   <li>javax.persistence.Embeddable</li>
*   <li>javax.persistence.MappedSuperclass</li>
*   <li>org.hibernate.annotations.Entity</li>
* </ul>
*/

@Name("hibernateSessionFactory")
@Scope(ScopeType.APPLICATION)
@BypassInterceptors
@Startup
public class AutoloadHibernateSessionFactory extends HibernateSessionFactory {
       
        private static Log log = LogFactory.getLog( HibernateSessionFactory.class );
       
        /**
         * Crée la session factory. Cette méthode surcharge la méthode du composant standard
         * de Seam, pour charger rechercher d'abord les classes persistantes d'après leurs
         * annotations.
         */
   protected SessionFactory createSessionFactory() throws ClassNotFoundException {

           List<String> autoloadClasses = new ArrayList<String>(100);
           try {
                   // Scan des classes de l'application
                   log.info("Scanning class files for annotations.");
                        URL[] urls = ClasspathUrlFinder.findResourceBases("hibernate.cfg.xml");
                        AnnotationDB db = new AnnotationDB();
                        db.scanArchives(urls);
                       
                        // Annotation : javax.persistence.Entity
                        autoloadClasses.addAll(searchClasses(db, javax.persistence.Entity.class));
                        // Annotation : javax.persistence.Embeddable
                        autoloadClasses.addAll(searchClasses(db, javax.persistence.Embeddable.class));
                        // Annotation : javax.persistence.MappedSuperclass
                        autoloadClasses.addAll(searchClasses(db, javax.persistence.MappedSuperclass.class));
                        // Annotation : org.hibernate.annotations.Entity
                        autoloadClasses.addAll(searchClasses(db, org.hibernate.annotations.Entity.class));
                       
                        this.setMappingClasses(autoloadClasses);
           } catch (IOException e) {
                   e.printStackTrace();
                   throw new IllegalStateException(e);
           }
     
           return super.createSessionFactory();
        }
   
   /**
    * Renvoie l'ensemble des noms des classes Java annotées avec l'annotation passée
    * en paramètre, après avoir écrit tous ces noms de classes dans le log.
    */
   private Set<String> searchClasses(AnnotationDB db, Class<?> annotation) {
                Set<String> classes = db.getAnnotationIndex().get(annotation.getName());
                if (classes==null) {
                        classes = new HashSet<String>();
                }
                for (String className : classes) {
                        log.info(String.format("Autoload @%s %s", annotation.getName(), className));
                }
                return classes;
   }
}

Voilà, pour l’utiliser il suffit d’enlever le composant fourni par Seam du fichier components.xml, et de vérifier que le nom déclaré dans cette classe (« hibernateSessionFactory ») correspond bien à celui indiqué dans l’attribut session-factory du composant persistence:managed-hibernate-session.




Laisser un commentaire
Un message, un commentaire ?

(Pour créer des paragraphes, laissez simplement des lignes vides.)

Lien hypertexte (optionnel)

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d'informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Qui êtes-vous ? (optionnel)


captcha




 
Copyright 2006-2008 IOcean  Plan du site  l  Lexique  l Informations légales  l Contacts l Recrutement