如何配置休眠来扫描不同模块中的实体

问题描述 投票:0回答:7

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

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

这发生在 EntityManager.merge 调用中。

由于模块 B 具有所有休眠配置文件等,我猜它根本没有意识到我的 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>

这给了我一个错误,“属性”的内容必须匹配空。 然后我尝试了:

<mapping class="myNamespace.*" />

我错过了什么?

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

java hibernate maven jpa
7个回答
4
投票

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

除了:

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

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

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

它确定 Hibernate Entity Manager 自动发现哪个元素。

对于额外的实体(在其他 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 文件或目录称为持久化单元的根。持久性单元的范围由持久性单元的根确定。每个持久性单元必须使用该持久性单元范围内唯一的名称来标识。

问候,安德烈


2
投票
  • 如果您使用 hibernate/spring 我们可以扩展 LocalSessionFactoryBean 对象并扫描整个项目以 识别项目中的实体类。
  • 既然你说的是两个不同的项目,那么尝试写 一些构建时实用程序来解析两个项目并创建一个 解决您问题的实体 xml 文件。

1
投票

简单的方法来做到这一点

configuration.addAnnotatedClass(Contact.class)

如果您想使用按包扫描,请首先使用

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);
        }
    }

}

1
投票

我们通过使用带注释的 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...
}

0
投票

persistence.xml 可能包含

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


0
投票

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

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

希望有帮助。


-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 中

© www.soinside.com 2019 - 2024. All rights reserved.