Spring Test with JUnit 4 and Spring Data JPA: NoSuchMethodError org.hibernate.engine.spi.SessionFactoryImplementor.getProperties

你说的曾经没有我的故事 提交于 2020-01-05 04:52:21

问题


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:

  1. 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>
  1. Follow maven project structure conventions. In your case test related source code must be placed in src/test/java folder instead of src/main/test:
my-app
|-- pom.xml
`-- src
    |-- main
    |   `-- java
    |       `-- com
    |           `-- mycompany
    |               `-- app
    |                   `-- App.java
    `-- test
        `-- java
            `-- com
                `-- mycompany
                    `-- app
                        `-- AppTest.java
  1. It is good practise to define your own package name instead of just beans or config. For example it can be com.pneumokok.mvc, com.pneumokok.service, com.pneumokok.test and com.pneumokok.model. It can help you with component scan using base package name.

  2. As you rightly pointed out it is necessary to add javax.servlet-api dependency, but it is important to define provided scope

<dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
</dependency>
  1. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!