web-dev-qa-db-fra.com

Comment configurer hibernate pour rechercher des entités dans un module différent

J'ai les modules A et B qui ont tous deux des classes annotées JPA. Le module B a un test unitaire qui extrait quelques-unes de ces entités de A. Les deux modules se compilent bien, les dépendances d'exécution sont définies correctement, mais le message d'erreur suivant s'affiche lorsque j'essaie d'exécuter le test unitaire:

Java.lang.IllegalArgumentException: Unknown entity: MyClassHere
Caused by: org.hibernate.MappingException: Unknown entity: MyClassHere

Cela se produit dans l'appel EntityManager.merge.

Puisque le module B contient tous les fichiers de configuration d'hibernation, etc., je suppose que ce n'est tout simplement pas de prendre en compte que ma classe de A est une entité.

J'ai essayé d'ajouter ce qui suit à persistence.xml

<exclude-unlisted-classes>false</exclude-unlisted-classes>

Dans hibernate.cfg.xml, j'ai ajouté:

<property name="packagesToScan">myNamespace.*</property>

Ensuite:

 <property name="packagesToScan">
                <array>
                    <value>myNamespace.*</value>
                </array>
</property>

Cela m'a donné une erreur que le contenu de "propriété" doit correspondre à null . Puis j'ai essayé:

<mapping class="myNamespace.*" />

Qu'est-ce que je rate? 

Edit: Une chose que j'ai oublié de mentionner et qui pourrait être importante est que les deux modules sont configurés en tant que projets séparés (j'utilise Eclipse), de sorte que la structure de répertoires est différente. Les dépendances d'exécution sont toutes configurées correctement, mais comme les fichiers .class se retrouvent dans des répertoires différents, je pense que le mode veille prolongée pourrait ne pas les analyser.

19
ventsyv
  • Si vous utilisez hibernate/spring, nous pouvons étendre l'objet LocalSessionFactoryBean et analyser le projet pour identifier les classes d'entité Dans celui-ci. 
  • Puisque vous dites deux projets différents, essayez alors d’écrire Un utilitaire de compilation pour analyser les deux projets et créer un fichier Entity xml permettant de résoudre votre problème.
2
BValluri

Si vous configurez votre projet pour qu'il détecte automatiquement les entités, il n'analysera que le chemin où se trouve le fichier META-INF/persistence.xml (par défaut).

En plus de :

<exclude-unlisted-classes>false</exclude-unlisted-classes>

Vous définissez une option d'hibernation supplémentaire:

<property name="hibernate.archive.autodetection" value="class, hbm" />

Il détermine quel élément est automatiquement détecté par Hibernate Entity Manager.

Pour les entités supplémentaires (dans les autres jars), vous pouvez définir la section jar-file dans votre fichier persistence.xml principal:

<persistence>
    <persistence-unit name="myUnit">
        ...
        <class>foo.bar.Entity1</class>
        <class>foo.bar.Entity2</class>
        <jar-file>moduleB.jar</jar-file>
        ...
    </persistence-unit>
</persistence>

L'élément jar-file spécifie les fichiers JAR visibles de l'unité de persistance empaquetée contenant les classes de persistance gérées, tandis que l'élément class nomme explicitement les classes de persistance gérées.

Le fichier ou le répertoire JAR dont le répertoire META-INF contient persistence.xml est appelé la racine de l'unité de persistance. La portée de l’unité de persistance est déterminée par la racine de celle-ci. Chaque unité de persistance doit être identifiée par un nom unique dans son champ d’application.

Cordialement, André

2
André Blaszczyk

J'ai récemment résolu un problème similaire en ajoutant le chemin d'accès au fichier persistence.xml.

<jar-file>file:///C:/yourpath/yourJar.jar</jar-file>

J'espère que ça aide.

0
Rafael Marins

persistence.xml peut contenir l'élément <jar-file>....jar</jar-file>: spécifie un ou plusieurs fichiers JAR dans lesquels les classes seront recherchées.

0
sibnick

Le moyen simple de faire ça

configuration.addAnnotatedClass(Contact.class)

si vous souhaitez utiliser l'analyse par package, chargez d'abord toutes les classes à l'aide de ClassLoader. Voir l'exemple de code source tiré de Hibernate-orm LocalSessionFactoryBuilder.class

@Bean
public SessionFactory sessionFactory(){

    HibernateConfig configuration = new HibernateConfig();

    Properties properties = hibernateProperties();

    configuration.setProperties(properties);

    configuration.scanPackages("com.atcc.stom.model.entity");

    return configuration.buildSessionFactory();
}

HibernateConfig.class  

import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.cfg.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;

import javax.persistence.AttributeConverter;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import Java.io.IOException;
import Java.lang.annotation.Annotation;
import Java.util.Set;
import Java.util.TreeSet;

public class HibernateConfig extends Configuration {

    private static final TypeFilter[] DEFAULT_ENTITY_TYPE_FILTERS = new TypeFilter[] {
            new AnnotationTypeFilter(Entity.class, false),
            new AnnotationTypeFilter(Embeddable.class, false),
            new AnnotationTypeFilter(MappedSuperclass.class, false)};

    private static final String RESOURCE_PATTERN = "/**/*.class";

    private static final String PACKAGE_INFO_SUFFIX = ".package-info";

    private final ResourcePatternResolver resourcePatternResolver;

    private static TypeFilter converterTypeFilter;

    static {
        try {
            @SuppressWarnings("unchecked")
            Class<? extends Annotation> converterAnnotation = (Class<? extends Annotation>)
                    ClassUtils.forName("javax.persistence.Converter", Configuration.class.getClassLoader());
            converterTypeFilter = new AnnotationTypeFilter(converterAnnotation, false);
        }
        catch (ClassNotFoundException ex) {
            // JPA 2.1 API not available - Hibernate <4.3
        }
    }

    public HibernateConfig() {
        this(new PathMatchingResourcePatternResolver());
    }

    public HibernateConfig(ClassLoader classLoader) {
        this(new PathMatchingResourcePatternResolver(classLoader));
    }

    public HibernateConfig(ResourceLoader resourceLoader) {
        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
    }

    public void scanPackages(String... packagesToScan) throws HibernateException {
        Set<String> entityClassNames = new TreeSet<String>();
        Set<String> converterClassNames = new TreeSet<String>();
        Set<String> packageNames = new TreeSet<String>();
        try {
            for (String pkg : packagesToScan) {
                String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                        ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN;

                Resource[] resources = this.resourcePatternResolver.getResources(pattern);
                MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
                for (Resource resource : resources) {
                    if (resource.isReadable()) {
                        MetadataReader reader = readerFactory.getMetadataReader(resource);
                        String className = reader.getClassMetadata().getClassName();
                        if (matchesEntityTypeFilter(reader, readerFactory)) {
                            entityClassNames.add(className);
                        }
                        else if (converterTypeFilter != null && converterTypeFilter.match(reader, readerFactory)) {
                            converterClassNames.add(className);
                        }
                        else if (className.endsWith(PACKAGE_INFO_SUFFIX)) {
                            packageNames.add(className.substring(0, className.length() - PACKAGE_INFO_SUFFIX.length()));
                        }
                    }
                }
            }
        }
        catch (IOException ex) {
            throw new MappingException("Failed to scan classpath for unlisted classes", ex);
        }
        try {
            ClassLoader cl = this.resourcePatternResolver.getClassLoader();
            for (String className : entityClassNames) {
                addAnnotatedClass(cl.loadClass(className));
            }
            for (String className : converterClassNames) {
                ConverterRegistrationDelegate.registerConverter(this, cl.loadClass(className));
            }
            for (String packageName : packageNames) {
                addPackage(packageName);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new MappingException("Failed to load annotated classes from classpath", ex);
        }
    }

    private boolean matchesEntityTypeFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
        for (TypeFilter filter : DEFAULT_ENTITY_TYPE_FILTERS) {
            if (filter.match(reader, readerFactory)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Inner class to avoid hard dependency on JPA 2.1 / Hibernate 4.3.
     */
    private static class ConverterRegistrationDelegate {

        @SuppressWarnings("unchecked")
        public static void registerConverter(Configuration config, Class<?> converterClass) {
            config.addAttributeConverter((Class<? extends AttributeConverter<?, ?>>) converterClass);
        }
    }

}
0
Vahe Gharibyan

Nous avons résolu un problème similaire sur le projet sur lequel je travaille en utilisant Spring pour détecter les entités. Par exemple. en utilisant une configuration Spring annotée:

@Configuration
@ComponentScan("com.org.prj.myNamespace1", "com.org.prj.myNamespace2")
public class MyDatabaseConfig {
    @Bean
    public EntityManagerFactory entityManagerFactory() {
        final LocalContainerEntityManagerFactoryBean factory =
            new LocalContainerEntityManagerFactoryBean();

        // ...JPA properties, vendor adaptor, dialect, data source, persistence unit etc...

        factory.setPackagesToScan("com.org.prj.myNamespace1", "com.org.prj.myNamespace2");

        factory.afterPropertiesSet();
        return factory.getObject();
    }

    // ...Data source beans etc...
}
0
Steve Chambers