如何配置hibernate以扫描不同模块中的实体

时间:2015-10-23 15:13:35

标签: java hibernate maven jpa

我有模块A和模块B,它们都有JPA注释类。模块B有一个单元测试,从A中抽取几个实体。 两个模块编译正常,运行时依赖项设置正常,但是当我尝试运行单元测试时出现以下错误:

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

这发生在EntityManager.merge调用中。

由于模块B具有所有hibernate配置文件等,我猜测它根本没有意识到我的A类是实体。

我尝试将以下内容添加到persistence.xml

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

在hibernate.cfg.xml中我添加了:

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

然后:

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

这给了我一个错误,即&#34; property&#34;必须匹配null。 然后我试了一下:

<mapping class="myNamespace.*" />

我错过了什么?

编辑:我忘了提到的一件可能有意义的事情就是将这两个模块设置为单独的项目(我使用的是eclipse),因此目录结构不同。运行时依赖项都已正确设置,但由于.class文件最终位于不同的目录中,我认为hibernate可能不会扫描那些。

7 个答案:

答案 0 :(得分:2)

如果将项目配置为自动检测实体,它将仅扫描META-INF / persistence.xml所在的路径(默认情况下)。

除了:

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

您设置了一个额外的休眠选项:

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

它确定Hibernate实体管理器自动发现哪个元素。

对于额外的实体(在其他jar中),您可以在主persistence.xml文件中设置 jar-file 部分:

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

jar-file元素指定对包含托管持久性类的打包持久性单元可见的JAR文件,而class元素显式命名托管持久性类。

其META-INF目录包含persistence.xml的JAR文件或目录称为持久性单元的根。持久性单元的范围由持久性单元的根确定。必须使用持久性单元范围唯一的名称来标识每个持久性单元。

问候,André

答案 1 :(得分:2)

  • 如果您正在使用hibernate / spring,我们可以扩展 LocalSessionFactoryBean对象并通过项目扫描到 识别项目中的实体类。
  • 既然你说两个不同的项目,那就试着写 一些构建时间实用程序来解析这两个项目并创建一个 解决问题的实体xml文件。

答案 2 :(得分:0)

persistence.xml可能包含<jar-file>....jar</jar-file>元素:指定将搜索类的一个或多个JAR文件。

答案 3 :(得分:0)

我们通过使用Spring来检测实体,解决了我正在进行的项目中的类似问题。例如。使用带注释的Spring配置:

@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...
}

答案 4 :(得分:0)

我最近解决了类似的问题,将jar路径添加到文件persistence.xml

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

希望它有所帮助。

答案 5 :(得分:0)

这样做的简单方法

configuration.addAnnotatedClass(Contact.class)

如果您想使用scan by package,请先使用ClassLoader加载所有类。请参阅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);
        }
    }

}

答案 6 :(得分:-1)

  • src / main / resources:applicationContext.xml
  • src / test / resources:test-applicationContext.xml

确保在测试范围内还创建应用程序上下文以扫描这些实体。您的test-applicationContext.xml可能不会在运行时设置整个应用程序上下文,但是也应该包括在测试时也需要的一些内容,例如包扫描。

您可以在src / main / resources中创建一个persistence.xml,并将其包含在applicationContext.xml和test-applicationContext.xml中