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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33305823/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 14:34:06  来源:igfitidea点击:

How to configure hibernate to scan for entities in a different module

javahibernatemavenjpa

提问by ventsyv

I have module A and module B which both have JPA annotated classes. Module B has an unit test that pulls in a couple of those entities from A. Both modules compile fine, the runtime dependencies are set OK, but I get the following error when I try to run the unit test:

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

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

This occurs in the EntityManager.merge call.

这发生在 EntityManager.merge 调用中。

Since module B has all the hibernate config files etc, I'm guessing it's simply not picking up that my class from A is an entity.

由于模块 B 具有所有休眠配置文件等,我猜它根本没有从 A 中获取我的类是一个实体。

I tried adding the following to persistence.xml

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

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

In hibernate.cfg.xml I added:

在 hibernate.cfg.xml 我添加:

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

Then:

然后:

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

That gave me an error that content of "property" must match null. Then I tried:

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

<mapping class="myNamespace.*" />

What am I missing?

我错过了什么?

Edit:One thing that I forgot to mention that might be of significance is that the two modules are set up as separate projects (I'm using eclipse) so the directory structure is different. The run time dependencies are all set up correctly but since the .class files end up in different directories, I think hibernate might not be scanning those.

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

回答by André Blaszczyk

If you configure your project to autodetect the entities, it will only scan the path where the META-INF/persistence.xml is (by default).

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

In addition to :

此外 :

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

You set an extra hibernate option :

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

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

It determines which element is auto discovered by Hibernate Entity Manager.

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

For extra entities (in others jars), you can set the jar-filesection in your main persistence.xml file :

对于额外的实体(在其他 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>

The jar-file element specifies JAR files that are visible to the packaged persistence unit that contain managed persistence classes, whereas the class element explicitly names managed persistence classes.

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

The JAR file or directory whose META-INF directory contains persistence.xml is called the root of the persistence unit. The scope of the persistence unit is determined by the persistence unit's root. Each persistence unit must be identified with a name that is unique to the persistence unit's scope.

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

Regards, André

问候, 安德烈

回答by sibnick

persistence.xml may contain <jar-file>....jar</jar-file>element: specifies one or more JAR files that will be searched for classes.

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

回答by Steve Chambers

We solved a similar problem on the project I'm working on by using Springto detect the entities. E.g. using an annotated Spring configuration:

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

回答by Rafael Marins

I recently solved a similar problem, adding the jar path to the file persistence.xml

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

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

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

Hope it helps.  

希望能帮助到你。  

回答by BValluri

  • If you are using hibernate/spring we can extend the LocalSessionFactoryBean object and scan through the project to identify entity classes in the project.
  • Since you are saying two different projects, then try to write some build time utility to parse the two projects and created one entity xml file that solve your problem.
  • 如果您使用的是 hibernate/spring,我们可以扩展 LocalSessionFactoryBean 对象并扫描项目以识别项目中的实体类。
  • 既然您说的是两个不同的项目,那么尝试编写一些构建时实用程序来解析这两个项目并创建一个实体 xml 文件来解决您的问题。

回答by Fico

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

Make sure in the test-scope you also create your application-context to scan for those entities. Your test-applicationContext.xml might not set-up the entire application-context as at runtime, but some things which are also needed at test-time should also there be included, like your package-scanning.

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

You can ofcourse create a persistence.xml in src/main/resources and include it in both the applicationContext.xml and test-applicationContext.xml

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

回答by Vahe Gharibyan

The simple way to do that

这样做的简单方法

configuration.addAnnotatedClass(Contact.class)

if you would like to use scan by package, first load all classes using ClassLoader. See the example source code from Hibernate-ormLocalSessionFactoryBuilder.class

如果您想使用按包扫描,请首先使用ClassLoader. 请参阅Hibernate-ormLocalSessionFactoryBuilder.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

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

}