我发现了 2 种可能性,而无需更改类加载器/使用其他 Maven 插件/配置文件/复制覆盖文件。
TL;DR:检查提供商名称。
起初我开始以编程方式构建 entityManagerFactory,如下所示:create entity manager programmatically without persistence file。
所以我做了非常相似的事情:
@BeforeClass
public static void prepare() {
Map<String, Object> configOverrides = new HashMap<>();
configOverrides.put("hibernate.connection.driver_class", "org.h2.Driver");
configOverrides.put("hibernate.connection.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
configOverrides.put("hibernate.connection.username", "sa");
configOverrides.put("hibernate.connection.password", "sa");
configOverrides.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
configOverrides.put("hibernate.show_sql", "true");
configOverrides.put("hibernate.hbm2ddl.auto", "validate");
factory = new HibernatePersistence().createContainerEntityManagerFactory(
new CustomPersistenceUnitInfo(), configOverrides
);
//factory = Persistence.createEntityManagerFactory("test");
assertNotNull(factory);
}
...
private static class CustomPersistenceUnitInfo implements PersistenceUnitInfo {
@Override
public String getPersistenceUnitName() {
return "test";
}
@Override
public String getPersistenceProviderClassName() {
return "org.hibernate.jpa.HibernatePersistenceProvider";
// <------------note here: this is wrong!
}
@Override
public PersistenceUnitTransactionType getTransactionType() {
return PersistenceUnitTransactionType.RESOURCE_LOCAL;
}
@Override
public DataSource getJtaDataSource() {
return null;
}
@Override
public DataSource getNonJtaDataSource() {
return null;
}
@Override
public List<String> getMappingFileNames() {
return Collections.emptyList();
}
@Override
public List<URL> getJarFileUrls() {
try {
return Collections.list(this.getClass()
.getClassLoader()
.getResources(""));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public URL getPersistenceUnitRootUrl() {
return null;
}
@Override
public List<String> getManagedClassNames() {
return Arrays.asList(
"com.app.Entity1",
"com.app.Entity2"
);
}
@Override
public boolean excludeUnlistedClasses() {
return true;
}
@Override
public SharedCacheMode getSharedCacheMode() {
return null;
}
@Override
public ValidationMode getValidationMode() {
return null;
}
@Override
public Properties getProperties() {
return null;
}
@Override
public String getPersistenceXMLSchemaVersion() {
return null;
}
@Override
public ClassLoader getClassLoader() {
return null;
}
@Override
public void addTransformer(final ClassTransformer classTransformer) {
}
@Override
public ClassLoader getNewTempClassLoader() {
return null;
}
}
然后,我发现它仍然返回null。为什么?
然后我发现当我使用com.hibernate.ejb.HibernatePersistence类的时候,provider应该不是com.hibernate.jpa.HibernatePersistenceProvider,而是com.hibernate.ejb.HibernatePersistence。 HibernatePersistenceProvider 类甚至在 IDEA“开放类”中都找不到,即使它在主 persistence.xml 中也是如此。
在Ejb3Configuration.class我发现:
integration = integration != null ? Collections.unmodifiableMap(integration) : CollectionHelper.EMPTY_MAP;
String provider = (String)integration.get("javax.persistence.provider");
if (provider == null) {
provider = info.getPersistenceProviderClassName();
}
if (provider != null && !provider.trim().startsWith(IMPLEMENTATION_NAME)) { // private static final String IMPLEMENTATION_NAME = HibernatePersistence.class.getName(); which, is, "com.hibernate.ejb.HibernatePersistence"
LOG.requiredDifferentProvider(provider);
return null;
} else {
所以我回到persistence.xml 的第一个解决方案,并更改提供者名称,现在它可以工作了。似乎即使 main 中的提供者也是jpa.xxx,在测试中它不是。
因此,总而言之,需要检查 3 件事:
- 在 Maven 中打开
-X 以检查 maven-resources-plugin 是否真的将您的 src/test/resources/META-INF/persistence.xml 复制到 target/test-classes(我认为这永远不会失败)
- 检查
hibernate-entitymanager是否在你的类路径中(你可以用mvn dependency:tree -Dincludes=org.hibernate:hibernate-entitymanager检查。
- 检查提供商的名称,最重要的一项。应该是
org.hibernate.ejb.HibernatePersistence。
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.app.model.Company</class>
...