问题
With SpringJUnit4ClassRunner
, JUnit 4 and Spring Test I wrote unit test for Service
which uses Spring Data JPA Repository
and embedded HSQL database:
@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());
}
}
I run test with required javaagent option on VM, with config:
<?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>
But I got:
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...
The only difference between test config and applicationContext.xml
(which works for Tomcat app) is embedded database used in test, but even if I use dataSource
from project:
<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>
I still face the same issue (project works normally). Also, i don't think problem is that I don't have hibernate.properties
file, cause I asked about it here: Spring Data configuration - hibernate.properties not found. I use Spring 4.3.2.RELEASE
, with 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
and Spring Data Commons Core 1.4.1.RELEASE
. I will be happy if anybody would help me - thank you in advance.
UPDATE: I changed jpaProperties
to jpaPropertyMap
for 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>
And comment dependency for hibernate-entitymanager
, but it still does not work. Also I have the same issue when I switch to Hibernate 5.1
UPDATE 2: I created a Java config version, maybe it will help someone to see where I made a mistake:
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;
}
}
UPDATE 2016-10-04: I created Github repository which shows problem, there you will see that app itself works quite well (just add Actor
in form inside index,jsp
file, but it does not work for test). P.S. I deploy war:exploded
to Tomcat 8.0.24 local instance from Intellij IDEA ("Run" button).
回答1:
I checked your example on GitHub, and Unit-Test has worked for me after some code modifications. But first I wanted to say that it was little bit another Exception for me in the beginning. It was like follows:
Caused by: java.lang.ClassCastException: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean$$EnhancerBySpringCGLIB$$fedd095f cannot be cast to javax.persistence.EntityManagerFactory
JpaTransactionManager
configuration in HibernateConfig
need to be changed to fix this problem like follows:
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
Unlike the original config version in this case EntityManagerFactory
is injected as Spring managed bean, instead of separate instance that created with HibernateConfig.entityManagerFactory()
method.
Also other changes have been made, but they are not directly related to the Question subject. I can provide them too if you want.
回答2:
Using Hibernate Core 5.2 and Hibernate EntityManager 5.1 is very likely to cause the issue here. 5.2 moved the EntityManager
implementation into the core module so that you end up with 2 JPA implementations on the classpath which probably causes Spring framework to fail to detect the Hibernate version to bootstrap.
Make sure you either use Hibernate 5.1 and refer to the hibernate-entitymanager
artifact or 5.2 and only pull in hiberante-core
.
回答3:
While fixing this bug in hibernate community the signature of SessionFactoryImplementor.getProperties()
was changed in 5.2 to return Map
rather than Properties
. reference
Hence, you should use Map
.
You are using hibernate 5.2.0 Final, In the new version of hibernate 5.2.3 Final the community have consolidated few hibernate-entitymanager issues. download link. Please try with this version.
Suggestion:
1) Use the below hibernate-core and hibernate-entitymanager versions instead of 5.2.0.Final and 5.1.0.Final versions respectively.
<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) Revert to the Hibernate 5.1.x release (I guess you should have no issues with this.)
3) If the first & second suggestions didn't worked then move on to Release 6.0.1.GA, it is compatible with Hibernate 5.2. community discussion
4) Instead of below in config (just for the sake of trial and error method.)
<!-- 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>
Use this code:
<!-- 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>
回答4:
It can be out of topic but your sample project can be also improved with some additional changes to prevent problems in the future. This improvements below:
- Explicitly define versions and configuration for all Maven plugins you are using. At least it is
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>
- Follow maven project structure conventions. In your case
test
related source code must be placed insrc/test/java
folder instead ofsrc/main/test
:
my-app
|-- pom.xml
`-- src
|-- main
| `-- java
| `-- com
| `-- mycompany
| `-- app
| `-- App.java
`-- test
`-- java
`-- com
`-- mycompany
`-- app
`-- AppTest.java
It is good practise to define your own package name instead of just
beans
orconfig
. For example it can becom.pneumokok.mvc
,com.pneumokok.service
,com.pneumokok.test
andcom.pneumokok.model
. It can help you withcomponent scan
usingbase package
name.As you rightly pointed out it is necessary to add
javax.servlet-api
dependency, but it is important to defineprovided
scope
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
- Last but not least Use separate Spring context definition for each Application layer and environment. In your example several context definitions can be used:
src/main/resources/com/pneumokok/service/applicationContext.xml
Spring context configuration for Service layer
<?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 context configuration for MVC layer
<?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 context configuration for Servlet container environment
<?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 context configuration for Test environment
<?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>
来源:https://stackoverflow.com/questions/39815784/spring-test-with-junit-4-and-spring-data-jpa-nosuchmethoderror-org-hibernate-en