Spring 使用 JUnit 4 和 Spring 数据 JPA 进行测试:NoSuchMethodError org.hibernate.engine.spi.SessionFactoryImplementor.getProperties
Spring Test with JUnit 4 and Spring Data JPA: NoSuchMethodError org.hibernate.engine.spi.SessionFactoryImplementor.getProperties
使用 SpringJUnit4ClassRunner
、JUnit 4 和 Spring 测试我为 Service
编写了单元测试,它使用 Spring Data JPA Repository
和嵌入式 HSQL 数据库:
@Ignore
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:unitTestFullConfig.xml")
public class InMemoryDBFullTestBaseClass {
}
public final class ActorServiceImplTest extends InMemoryDBFullTestBaseClass {
@Inject
private ActorService service;
@Test
public final void saveActor () throws Exception {
service.save(new ActorDTO(null, "testName", "testSurname", new Date(), Collections.emptyList()));
assertEquals(1, service.getAll().size());
}
}
我运行测试required javaagent option on VM,配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Configure the data source bean -->
<jdbc:embedded-database id="dataSource" type="HSQL">
</jdbc:embedded-database>
<!-- Enable annotation driven transaction management -->
<tx:annotation-driven/>
<mvc:annotation-driven/>
<context:component-scan base-package="beans"/>
<!-- Create default configuration for Hibernate -->
<bean id="hibernateJpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
<!-- Configure the entity manager factory bean -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
<!-- Set JPA properties -->
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="javax.persistence.schema-generation.database.action">none</prop>
<prop key="hibernate.ejb.use_class_enhancer">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<!-- Set base package of your entities -->
<property name="packagesToScan" value="models"/>
<!-- Set share cache mode -->
<property name="sharedCacheMode" value="ENABLE_SELECTIVE"/>
<!-- Set validation mode -->
<property name="validationMode" value="NONE"/>
<property name="persistenceUnitName" value="testJPA" />
</bean>
<!-- Configure the transaction manager bean -->
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--
Configure Spring Data JPA and set the base package of the
repository interfaces
-->
<jpa:repositories base-package="beans.repositories"/>
</beans>
但是我得到了:
Error creating bean with name 'entityManagerFactory' defined in URL [file:/E:/workspace/film-site/out/test/main/unitTestFullConfig.xml]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.hibernate.engine.spi.SessionFactoryImplementor.getProperties()Ljava/util/Properties;
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean...
测试配置和 applicationContext.xml
(适用于 Tomcat 应用程序)之间的唯一区别是测试中使用的嵌入式数据库,但即使我使用项目中的 dataSource
:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.postgresql.Driver"/>
<property name="jdbcUrl" value="jdbc:postgresql://localhost:5432/film-site"/>
<property name="user" value="postgres"/>
<property name="password" value="postgres"/>
<property name="maxPoolSize" value="10"/>
<property name="maxStatements" value="0"/>
<property name="minPoolSize" value="5"/>
</bean>
我仍然面临同样的问题(项目正常运行)。另外,我不认为问题是我没有 hibernate.properties
文件,因为我在这里询问过它:。我使用 Spring 4.3.2.RELEASE
、Hibernate Core 5.2.0.Final
、Hibernate Entity Manager 5.1.0.Final
、Spring Data 1.10.2.RELEASE JPA
、Spring Data Commons 1.12.2.RELEASE
和 Spring Data Commons Core 1.4.1.RELEASE
。如果有人能帮助我,我会很高兴 - 提前谢谢你。
更新: 我将 jpaProperties
更改为 jpaPropertyMap
entityManagerFactory
:
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
<entry key="javax.persistence.schema-generation.database.action" value="none" />
<entry key="hibernate.ejb.use_class_enhancer" value="true" />
<entry key="hibernate.hbm2ddl.auto" value="create" />
<entry key="hibernate.show_sql" value="true" />
</map>
</property>
和hibernate-entitymanager
的评论依赖,但它仍然不起作用。当我切换到 Hibernate 5.1 时,我也遇到了同样的问题
更新 2: 我创建了一个 Java 配置版本,也许它会帮助别人看到我在哪里犯了错误:
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import javax.persistence.EntityManagerFactory;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class HibernateConfig {
@Bean
public DataSource dataSource () {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build();
}
// Create default configuration for Hibernate
@Bean
public JpaVendorAdapter jpaVendorAdapter () {
return new HibernateJpaVendorAdapter();
}
// Configure the entity manager factory bean
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory () {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource());
factory.setJpaVendorAdapter(jpaVendorAdapter());
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
factory.setJpaPropertyMap(createJpaProperties());
factory.setPackagesToScan("models");
factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
factory.setValidationMode(ValidationMode.NONE);
factory.setPersistenceUnitName("testJPA");
return factory;
}
@Bean
public JpaTransactionManager transactionManager () {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory((EntityManagerFactory) entityManagerFactory());
return transactionManager;
}
private Map<String, ?> createJpaProperties () {
Map<String, Object> propertyMap = new HashMap();
propertyMap.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
propertyMap.put("javax.persistence.schema-generation.database.action", "none");
propertyMap.put("hibernate.ejb.use_class_enhancer", true);
propertyMap.put("hibernate.hbm2ddl.auto", "create");
propertyMap.put("hibernate.show_sql", true);
return propertyMap;
}
}
更新 2016-10-04:我创建了 Github repository which shows problem,在那里你会看到应用程序本身运行良好(只需在表单中添加 Actor
在 index,jsp
文件中,但它不适用于测试)。 P.S。我从 Intellij IDEA("Run" 按钮)将 war:exploded
部署到 Tomcat 8.0.24 本地实例。
在 hibernate 社区中修复此错误时,SessionFactoryImplementor.getProperties()
的签名在 5.2 中更改为 return Map
而不是 Properties
. reference
因此,您应该使用 Map
.
您使用的是hibernate 5.2.0 Final,在新版本hibernate 5.2.3 Final社区中整合了几个hibernate -entitymanager 问题。 download link。请尝试使用此版本。
建议:
1) 使用下面的 hibernate-core 和 hibernate-entitymanager 版本代替 5.2.0.Final 和 5.1.0.Final个版本。
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.2.1.Final</version>
</dependency>
2) 恢复到 Hibernate 5.1.x 发布(我想你应该对此没有问题。 )
3) 如果第一个和第二个建议没有奏效,那么继续发布 6.0.1.GA,它与 Hibernate 兼容5.2. community discussion
4) 而不是下面的配置(只是为了试错法。)
<!-- Configure the entity manager factory bean -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
<!-- Set JPA properties -->
<property name="jpaPropertyMap">
<map>
...
</map>
</property>
...
</bean>
使用此代码:
<!-- Configure the entity manager factory bean -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
<property name="jpaPropertyMap" ref="jpaPropertyMap" />
...
</bean>
<util:map id="jpaPropertyMap" map-class="java.util.TreeMap"> <!-- OR <util:map id="jpaPropertyMap"> OR <util:map id="jpaPropertyMap" map-class="java.util.HashMap">-->
<entry key="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
...
<entry key="hibernate.show_sql" value="true"/>
...
</util:map>
使用 Hibernate Core 5.2 和 Hibernate EntityManager 5.1 很可能会导致此处出现问题。 5.2 将 EntityManager
实现移到了核心模块中,这样您最终会在类路径上得到 2 个 JPA 实现,这可能会导致 Spring 框架无法检测到 bootstrap 的 Hibernate 版本。
确保您使用 Hibernate 5.1 并参考 hibernate-entitymanager
神器 或 5.2 并且仅引入 hiberante-core
.
我在 GitHub 上检查了您的示例,经过一些代码修改后单元测试对我有效。但首先我想说一开始对我来说这只是另一个例外。是这样的:
Caused by: java.lang.ClassCastException: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean$$EnhancerBySpringCGLIB$$fedd095f cannot be cast to javax.persistence.EntityManagerFactory
JpaTransactionManager
HibernateConfig
中的配置需要更改以解决此问题,如下所示:
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
与原始配置版本不同,在这种情况下 EntityManagerFactory
被注入为 Spring 托管 bean,而不是使用 HibernateConfig.entityManagerFactory()
方法创建的单独实例。
还进行了其他更改,但它们与问题主题没有直接关系。如果你需要我也可以提供。
它可能不合主题,但您的示例项目也可以通过一些额外的更改来改进,以防止将来出现问题。此改进如下:
- 为您使用的所有 Maven 插件明确定义版本和配置。至少是
Maven Compiler Plugin
:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.source}</source>
<target>${java.target}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
- 遵循 Maven 项目结构约定。在你的情况下
test
相关的源代码必须放在 src/test/java
文件夹而不是 src/main/test
:
my-app
|-- pom.xml
`-- src
|-- main
| `-- java
| `-- com
| `-- mycompany
| `-- app
| `-- App.java
`-- test
`-- java
`-- com
`-- mycompany
`-- app
`-- AppTest.java
最好定义自己的包名称,而不仅仅是 beans
或 config
。例如,它可以是 com.pneumokok.mvc
、com.pneumokok.service
、com.pneumokok.test
和 com.pneumokok.model
。它可以帮助您 component scan
使用 base package
名称。
正如您正确指出的那样,有必要添加 javax.servlet-api
依赖项,但定义 provided
范围
很重要
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
- 最后但并非最不重要的一点 为每个应用程序层和环境使用单独的 Spring 上下文定义。在您的示例中,可以使用多个上下文定义:
src/main/resources/com/pneumokok/service/applicationContext.xml
Spring服务层的上下文配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.pneumokok.service"/>
<!-- Enable annotation driven transaction management -->
<tx:annotation-driven/>
...
</beans>
src/main/resources/com/pneumokok/mvc/applicationContext.xml
Spring MVC 层的上下文配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.pneumokok.mvc"/>
<!-- Enable annotation driven transaction management -->
<mvc:annotation-driven/>
...
</beans>
src/main/resources/com/pneumokok/applicationContext.xml
Spring Servlet 容器环境的上下文配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<import resource="com/pneumokok/service/applicationContext.xml"/>
<import resource="com/pneumokok/mvc/applicationContext.xml"/>
<import resource="com/pneumokok/dao/applicationContext.xml"/>
<bean name="dataSource">
//Servlet container DataSource configuration
</bean>
...
</beans>
src/main/resources/com/pneumokok/test/applicationContext.xml
Spring 测试环境的上下文配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<import resource="com/pneumokok/service/applicationContext.xml"/>
<import resource="com/pneumokok/mvc/applicationContext.xml"/>
<import resource="com/pneumokok/dao/applicationContext.xml"/>
<bean name="dataSource">
//Test DataSource configuration
</bean>
...
</beans>
使用 SpringJUnit4ClassRunner
、JUnit 4 和 Spring 测试我为 Service
编写了单元测试,它使用 Spring Data JPA Repository
和嵌入式 HSQL 数据库:
@Ignore
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:unitTestFullConfig.xml")
public class InMemoryDBFullTestBaseClass {
}
public final class ActorServiceImplTest extends InMemoryDBFullTestBaseClass {
@Inject
private ActorService service;
@Test
public final void saveActor () throws Exception {
service.save(new ActorDTO(null, "testName", "testSurname", new Date(), Collections.emptyList()));
assertEquals(1, service.getAll().size());
}
}
我运行测试required javaagent option on VM,配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Configure the data source bean -->
<jdbc:embedded-database id="dataSource" type="HSQL">
</jdbc:embedded-database>
<!-- Enable annotation driven transaction management -->
<tx:annotation-driven/>
<mvc:annotation-driven/>
<context:component-scan base-package="beans"/>
<!-- Create default configuration for Hibernate -->
<bean id="hibernateJpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
<!-- Configure the entity manager factory bean -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
<!-- Set JPA properties -->
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="javax.persistence.schema-generation.database.action">none</prop>
<prop key="hibernate.ejb.use_class_enhancer">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<!-- Set base package of your entities -->
<property name="packagesToScan" value="models"/>
<!-- Set share cache mode -->
<property name="sharedCacheMode" value="ENABLE_SELECTIVE"/>
<!-- Set validation mode -->
<property name="validationMode" value="NONE"/>
<property name="persistenceUnitName" value="testJPA" />
</bean>
<!-- Configure the transaction manager bean -->
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--
Configure Spring Data JPA and set the base package of the
repository interfaces
-->
<jpa:repositories base-package="beans.repositories"/>
</beans>
但是我得到了:
Error creating bean with name 'entityManagerFactory' defined in URL [file:/E:/workspace/film-site/out/test/main/unitTestFullConfig.xml]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.hibernate.engine.spi.SessionFactoryImplementor.getProperties()Ljava/util/Properties; at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean...
测试配置和 applicationContext.xml
(适用于 Tomcat 应用程序)之间的唯一区别是测试中使用的嵌入式数据库,但即使我使用项目中的 dataSource
:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.postgresql.Driver"/>
<property name="jdbcUrl" value="jdbc:postgresql://localhost:5432/film-site"/>
<property name="user" value="postgres"/>
<property name="password" value="postgres"/>
<property name="maxPoolSize" value="10"/>
<property name="maxStatements" value="0"/>
<property name="minPoolSize" value="5"/>
</bean>
我仍然面临同样的问题(项目正常运行)。另外,我不认为问题是我没有 hibernate.properties
文件,因为我在这里询问过它:Spring 4.3.2.RELEASE
、Hibernate Core 5.2.0.Final
、Hibernate Entity Manager 5.1.0.Final
、Spring Data 1.10.2.RELEASE JPA
、Spring Data Commons 1.12.2.RELEASE
和 Spring Data Commons Core 1.4.1.RELEASE
。如果有人能帮助我,我会很高兴 - 提前谢谢你。
更新: 我将 jpaProperties
更改为 jpaPropertyMap
entityManagerFactory
:
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
<entry key="javax.persistence.schema-generation.database.action" value="none" />
<entry key="hibernate.ejb.use_class_enhancer" value="true" />
<entry key="hibernate.hbm2ddl.auto" value="create" />
<entry key="hibernate.show_sql" value="true" />
</map>
</property>
和hibernate-entitymanager
的评论依赖,但它仍然不起作用。当我切换到 Hibernate 5.1 时,我也遇到了同样的问题
更新 2: 我创建了一个 Java 配置版本,也许它会帮助别人看到我在哪里犯了错误:
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import javax.persistence.EntityManagerFactory;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class HibernateConfig {
@Bean
public DataSource dataSource () {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build();
}
// Create default configuration for Hibernate
@Bean
public JpaVendorAdapter jpaVendorAdapter () {
return new HibernateJpaVendorAdapter();
}
// Configure the entity manager factory bean
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory () {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource());
factory.setJpaVendorAdapter(jpaVendorAdapter());
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
factory.setJpaPropertyMap(createJpaProperties());
factory.setPackagesToScan("models");
factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
factory.setValidationMode(ValidationMode.NONE);
factory.setPersistenceUnitName("testJPA");
return factory;
}
@Bean
public JpaTransactionManager transactionManager () {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory((EntityManagerFactory) entityManagerFactory());
return transactionManager;
}
private Map<String, ?> createJpaProperties () {
Map<String, Object> propertyMap = new HashMap();
propertyMap.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
propertyMap.put("javax.persistence.schema-generation.database.action", "none");
propertyMap.put("hibernate.ejb.use_class_enhancer", true);
propertyMap.put("hibernate.hbm2ddl.auto", "create");
propertyMap.put("hibernate.show_sql", true);
return propertyMap;
}
}
更新 2016-10-04:我创建了 Github repository which shows problem,在那里你会看到应用程序本身运行良好(只需在表单中添加 Actor
在 index,jsp
文件中,但它不适用于测试)。 P.S。我从 Intellij IDEA("Run" 按钮)将 war:exploded
部署到 Tomcat 8.0.24 本地实例。
在 hibernate 社区中修复此错误时,SessionFactoryImplementor.getProperties()
的签名在 5.2 中更改为 return Map
而不是 Properties
. reference
因此,您应该使用 Map
.
您使用的是hibernate 5.2.0 Final,在新版本hibernate 5.2.3 Final社区中整合了几个hibernate -entitymanager 问题。 download link。请尝试使用此版本。
建议:
1) 使用下面的 hibernate-core 和 hibernate-entitymanager 版本代替 5.2.0.Final 和 5.1.0.Final个版本。
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.2.1.Final</version>
</dependency>
2) 恢复到 Hibernate 5.1.x 发布(我想你应该对此没有问题。 )
3) 如果第一个和第二个建议没有奏效,那么继续发布 6.0.1.GA,它与 Hibernate 兼容5.2. community discussion
4) 而不是下面的配置(只是为了试错法。)
<!-- Configure the entity manager factory bean -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
<!-- Set JPA properties -->
<property name="jpaPropertyMap">
<map>
...
</map>
</property>
...
</bean>
使用此代码:
<!-- Configure the entity manager factory bean -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
<property name="jpaPropertyMap" ref="jpaPropertyMap" />
...
</bean>
<util:map id="jpaPropertyMap" map-class="java.util.TreeMap"> <!-- OR <util:map id="jpaPropertyMap"> OR <util:map id="jpaPropertyMap" map-class="java.util.HashMap">-->
<entry key="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
...
<entry key="hibernate.show_sql" value="true"/>
...
</util:map>
使用 Hibernate Core 5.2 和 Hibernate EntityManager 5.1 很可能会导致此处出现问题。 5.2 将 EntityManager
实现移到了核心模块中,这样您最终会在类路径上得到 2 个 JPA 实现,这可能会导致 Spring 框架无法检测到 bootstrap 的 Hibernate 版本。
确保您使用 Hibernate 5.1 并参考 hibernate-entitymanager
神器 或 5.2 并且仅引入 hiberante-core
.
我在 GitHub 上检查了您的示例,经过一些代码修改后单元测试对我有效。但首先我想说一开始对我来说这只是另一个例外。是这样的:
Caused by: java.lang.ClassCastException: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean$$EnhancerBySpringCGLIB$$fedd095f cannot be cast to javax.persistence.EntityManagerFactory
JpaTransactionManager
HibernateConfig
中的配置需要更改以解决此问题,如下所示:
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
与原始配置版本不同,在这种情况下 EntityManagerFactory
被注入为 Spring 托管 bean,而不是使用 HibernateConfig.entityManagerFactory()
方法创建的单独实例。
还进行了其他更改,但它们与问题主题没有直接关系。如果你需要我也可以提供。
它可能不合主题,但您的示例项目也可以通过一些额外的更改来改进,以防止将来出现问题。此改进如下:
- 为您使用的所有 Maven 插件明确定义版本和配置。至少是
Maven Compiler Plugin
:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.source}</source>
<target>${java.target}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
- 遵循 Maven 项目结构约定。在你的情况下
test
相关的源代码必须放在src/test/java
文件夹而不是src/main/test
:
my-app
|-- pom.xml
`-- src
|-- main
| `-- java
| `-- com
| `-- mycompany
| `-- app
| `-- App.java
`-- test
`-- java
`-- com
`-- mycompany
`-- app
`-- AppTest.java
最好定义自己的包名称,而不仅仅是
beans
或config
。例如,它可以是com.pneumokok.mvc
、com.pneumokok.service
、com.pneumokok.test
和com.pneumokok.model
。它可以帮助您component scan
使用base package
名称。正如您正确指出的那样,有必要添加
javax.servlet-api
依赖项,但定义provided
范围 很重要
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
- 最后但并非最不重要的一点 为每个应用程序层和环境使用单独的 Spring 上下文定义。在您的示例中,可以使用多个上下文定义:
src/main/resources/com/pneumokok/service/applicationContext.xml
Spring服务层的上下文配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.pneumokok.service"/>
<!-- Enable annotation driven transaction management -->
<tx:annotation-driven/>
...
</beans>
src/main/resources/com/pneumokok/mvc/applicationContext.xml
Spring MVC 层的上下文配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.pneumokok.mvc"/>
<!-- Enable annotation driven transaction management -->
<mvc:annotation-driven/>
...
</beans>
src/main/resources/com/pneumokok/applicationContext.xml
Spring Servlet 容器环境的上下文配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<import resource="com/pneumokok/service/applicationContext.xml"/>
<import resource="com/pneumokok/mvc/applicationContext.xml"/>
<import resource="com/pneumokok/dao/applicationContext.xml"/>
<bean name="dataSource">
//Servlet container DataSource configuration
</bean>
...
</beans>
src/main/resources/com/pneumokok/test/applicationContext.xml
Spring 测试环境的上下文配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<import resource="com/pneumokok/service/applicationContext.xml"/>
<import resource="com/pneumokok/mvc/applicationContext.xml"/>
<import resource="com/pneumokok/dao/applicationContext.xml"/>
<bean name="dataSource">
//Test DataSource configuration
</bean>
...
</beans>