web-dev-qa-db-fra.com

Package de mise en veille prolongée

J'utilise Hibernate Annotations.

Dans toutes mes classes de modèles, j'annote comme ceci:

@Entity
@Table
public class SomeModelClass {
//
}

Mon hibernate.cfg.xml est

<hibernate-configuration>
   <session-factory>
      <!-- some properties -->

      <mapping package="com.fooPackage" />
      <mapping class="com.fooPackage.SomeModelClass" />
    </session-factory>
</hibernate-configuration>

Pour chaque classe que j'ajoute au com.fooPackage, je dois ajouter une ligne dans le hibernate.cfg.xml comme ceci:

<mapping class="com.fooPackage.AnotherModelClass" />

Existe-t-il un moyen d'ajouter de nouvelles classes de modèle, mais je n'ai pas besoin d'ajouter cette ligne à hibernate.cfg.xml?

39
Daniel Moura

Hors de la boîte - non. Vous pouvez cependant écrire votre propre code pour détecter/enregistrer vos classes annotées. Si vous utilisez Spring, vous pouvez étendre AnnotationSessionFactoryBean et faire quelque chose comme:

@Override
protected SessionFactory buildSessionFactory() throws Exception {
  ArrayList<Class> classes = new ArrayList<Class>();

  // the following will detect all classes that are annotated as @Entity
  ClassPathScanningCandidateComponentProvider scanner =
    new ClassPathScanningCandidateComponentProvider(false);
  scanner.addIncludeFilter(new AnnotationTypeFilter(Entity.class));

  // only register classes within "com.fooPackage" package
  for (BeanDefinition bd : scanner.findCandidateComponents("com.fooPackage")) {
    String name = bd.getBeanClassName();
    try {
      classes.add(Class.forName(name));
    } catch (Exception E) {
      // TODO: handle exception - couldn't load class in question
    }
  } // for

  // register detected classes with AnnotationSessionFactoryBean
  setAnnotatedClasses(classes.toArray(new Class[classes.size()]));
  return super.buildSessionFactory();
}

Si vous n'utilisez pas Spring (et vous devriez l'être :-)), vous pouvez écrire votre propre code pour détecter les classes appropriées et les enregistrer avec votre méthode AnnotationConfiguration via addAnnotatedClass().

Soit dit en passant, il n'est pas nécessaire de mapper les packages sauf si vous avez réellement déclaré quelque chose au niveau du package.

18
ChssPly76

Je viens de rencontrer ce problème, et de découvrir qu'il semble y avoir une solution prête à l'emploi pour cela. Mon intégration n'est pas encore sortie, la mettra à jour plus tard.

Depuis Javadoc, en particulier la partie packagesToScan:

org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean

Sous-classe de LocalSessionFactoryBean standard de Spring pour Hibernate, prenant en charge les métadonnées d'annotation JDK 1.5+ pour les mappages.

Remarque: Cette classe requiert Hibernate 3.2 ou une version ultérieure, avec la Java API Persistence et le module complémentaire Hibernate Annotations présents.

Exemple pour une définition de bean AnnotationSessionFactoryBean:

<bean id="sessionFactory" 
      class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  <property name="annotatedClasses">
    <list>
      <value>test.package.Foo</value>
      <value>test.package.Bar</value>
    </list>
  </property>
</bean>

Ou lors de l'utilisation de l'analyse du chemin de classe pour la détection automatique des classes d'entités:

<bean id="sessionFactory"
      class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  <property name="packagesToScan" value="test.package"/>
</bean>

Depuis: 1.2.2
Auteur: Juergen Hoeller

11
Jerry Tian

Un peu de nécromancie de fil ici ...

Il ne semble toujours pas qu'il existe un bon moyen de faire ce que vous voulez. Si vous ne souhaitez pas utiliser Spring, voici une méthode similaire utilisant Reflections :

// Create your SessionFactory with mappings for every `Entity` in a specific package
Configuration configuration = new Configuration();
configuration.configure("your_hibernate.cfg.xml");

Reflections reflections = new Reflections("your_package");

Set<Class<?>> classes = reflections.getTypesAnnotatedWith(javax.persistence.Entity.class);

for(Class<?> clazz : classes)
{
    configuration.addAnnotatedClass(clazz);
}

ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);

La même approche est probablement viable pour d'autres bibliothèques de réflexion Java.

11
luke

J'ai fait quelques recherches sur les approches d'analyse de classe, en utilisant les réponses de StackOverflow. Je rassemble donc tout cela, en utilisant une analyse des entités Hibernate comme test, dans le projet de test: hibernate-scanners-test .

Utilisation de l'hibernation fluide

Si vous recherchez une approche de numérisation rapide sans dépendances supplémentaires, vous pouvez essayer la bibliothèque fluent-hibernate (vous n'aurez pas besoin d'avoir d'autres pots, sauf la bibliothèque). En dehors de cela, il a quelques fonctionnalités utiles pour Hibernate 5 et Hibernate 4, y compris l'analyse des entités, une stratégie de nommage implicite Hibernate 5, un transformateur imbriqué et autres.

Téléchargez simplement la bibliothèque à partir de la page du projet: fluent-hibernate et utilisez EntityScanner :

Pour Hibernate 4 et Hibernate 5:

Configuration configuration = new Configuration();
EntityScanner.scanPackages("my.com.entities", "my.com.other.entities")
    .addTo(configuration);
SessionFactory sessionFactory = configuration.buildSessionFactory();

Utilisation d'une nouvelle API d'amorçage Hibernate 5:

List<Class<?>> classes = EntityScanner
        .scanPackages("my.com.entities", "my.com.other.entities").result();

MetadataSources metadataSources = new MetadataSources();
for (Class<?> annotatedClass : classes) {
    metadataSources.addAnnotatedClass(annotatedClass);
}

SessionFactory sessionFactory = metadataSources.buildMetadata()
    .buildSessionFactory();
1
v.ladynev

Si vous ne souhaitez pas utiliser spring ou toute autre bibliothèque, vous pouvez le faire comme ceci. Même approche que luke's mais sans bibliothèque Reflections

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

import javax.persistence.Entity;
import javax.tools.FileObject;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import Java.io.File;
import Java.io.IOException;
import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.Collections;
import Java.util.regex.Pattern;
import Java.util.stream.Collectors;
import Java.util.stream.StreamSupport;

public class SessionFactoryWrapper {

    private final SessionFactory sessionFactory;

    public SessionFactoryWrapper(final String...packagesToScan) {
        this.sessionFactory = this.createSessionFactory(packagesToScan);
    }

    private SessionFactory createSessionFactory(final String[] packagesToScan) {
        final Configuration configuration = new Configuration();
        configuration.configure(); // Reads hibernate.cfg.xml from classpath

        for (String packageToScan : packagesToScan) {
            this.getEntityClasses(packageToScan).stream().forEach( configuration::addAnnotatedClass);
        }

        final ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
        return configuration.buildSessionFactory(serviceRegistry);
    }

    private Collection<Class> getEntityClasses(final String pack) {
        final StandardJavaFileManager fileManager = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null);
        try {
            return StreamSupport.stream(fileManager.list(StandardLocation.CLASS_PATH, pack, Collections.singleton(JavaFileObject.Kind.CLASS), false).spliterator(), false)
                    .map(FileObject::getName)
                    .map(name -> {
                        try {
                            final String[] split = name
                                    .replace(".class", "")
                                    .replace(")", "")
                                    .split(Pattern.quote(File.separator));

                            final String fullClassName = pack + "." + split[split.length - 1];
                            return Class.forName(fullClassName);
                        } catch (ClassNotFoundException e) {
                            throw new RuntimeException(e);
                        }

                    })
                    .filter(aClass -> aClass.isAnnotationPresent(Entity.class))
                    .collect(Collectors.toCollection(ArrayList::new));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}
1
bhdrkn