我有模块 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 可能不会扫描这些文件。
如果您将项目配置为自动检测实体,它将仅扫描 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 文件或目录称为持久化单元的根。持久性单元的范围由持久性单元的根确定。每个持久性单元必须使用该持久性单元范围内唯一的名称来标识。
问候,安德烈
简单的方法来做到这一点
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);
}
}
}
我们通过使用带注释的 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...
}
persistence.xml 可能包含
<jar-file>....jar</jar-file>
元素:指定将搜索类的一个或多个 JAR 文件。
我最近解决了类似的问题,将jar路径添加到文件persistence.xml
<jar-file>file:///C:/yourpath/yourJar.jar</jar-file>
希望有帮助。
确保在测试范围内您还创建了应用程序上下文来扫描这些实体。您的 test-applicationContext.xml 可能不会像运行时那样设置整个应用程序上下文,但是测试时也需要的一些东西也应该包括在内,例如您的包扫描。
您当然可以在 src/main/resources 中创建 persistence.xml 并将其包含在 applicationContext.xml 和 test-applicationContext.xml 中