1

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).

Community
  • 1
  • 1
Radek Anuszewski
  • 1,812
  • 8
  • 34
  • 62
  • when you say its running fine , which application server do you use? is it shipped with the hibernate in its shared lib? – AntJavaDev Oct 04 '16 at 21:16
  • @AntJavaDev I deploy `war:exploded` to Tomcat 8.0.24 local instance from Intellij IDEA ("Run" button), I am not sure but I don't think Tomcat has Hibernate in shared lib, I don't even think about it earlier :) – Radek Anuszewski Oct 04 '16 at 21:25
  • ok , your github example isnt so valid , but anyway , i managed to run it locally with the hibernate version you have applied ( 4.3.11.Final and not 5.# which you are referring to your post) and it runs in both Tomcat 8 and JUnit runner. I also got it running under hibernate 5.1.0.Final and 5.2.0.Final which is mentioned in the answers. – AntJavaDev Oct 04 '16 at 21:59
  • so there must be an issue either with your classpath , or with your maven repo. Also when you find your issue , and you are ready to go to hibernate 5.2.0.Final mentioned by @Rohit Gaikwad , check this [link](https://jira.spring.io/browse/SPR-14334) , because you might have to upgrade spring version's as well – AntJavaDev Oct 04 '16 at 22:04

4 Answers4

3

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.

Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
  • Thank you, but error still occurs even when I comment `entity-manager` in `pom.xml`. Maybe @Rohit Gaikwad is right, but I don't know how to and where use `Map` rather than properties, I changed `jpaProperties` to `jpaPropertyMap` and even combined with your answer it didn't work. – Radek Anuszewski Oct 02 '16 at 11:56
  • Also I have the same issue when I switch to Hibernate 5.1 – Radek Anuszewski Oct 02 '16 at 12:15
  • When you switched to 5.1 have you reverted back from Map to properties in config? – Rohit Gaikwad Oct 06 '16 at 12:24
1

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>
Rohit Gaikwad
  • 3,677
  • 3
  • 17
  • 40
  • Hmm interesting, and is possible to do something about it? I changed `jpaProperties` to `jpaPropertyMap` (with `` etc) for `entityManagerFactory` and I still got the same issue. – Radek Anuszewski Oct 02 '16 at 10:51
  • Actually hibernate 5.2.x is the latest version, may be from now onwards they want us to use Map. So, instead of using xml configuration try to configure hibernate programmatically. here you can make use of different Maps like HashMap. – Rohit Gaikwad Oct 02 '16 at 13:20
  • I created a Java config version in my post, maybe I am a newbie - but still I don't know where `Map` should be used - `jpaPropertyMap` is a `Map` and I don't see another place. – Radek Anuszewski Oct 02 '16 at 19:24
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.

Sergey Bespalov
  • 1,746
  • 1
  • 12
  • 29
  • Could you please provide your changes? Because when I opened my code as new project in Intellij it went crazy (for example, I got `java.lang.ClassNotFoundException: javax.servlet.ServletContext` when I tried to run tests) and I think it shows real reason of my problems.. – Radek Anuszewski Oct 09 '16 at 17:28
  • After few hours of changing dependencies versions, `clean install`s and Intellij reinstalls, it worked! And you were right, `JpaTransactionManager` configuration had to be changed to yours. And still I will be happy if you would provide your changes, maybe I will be able to add some improvements to my code. – Radek Anuszewski Oct 09 '16 at 19:41
  • @Pneumokok modifications and recommendations provided in separate [answer](http://stackoverflow.com/questions/39815784/spring-test-with-junit-4-and-spring-data-jpa-nosuchmethoderror-org-hibernate-en/39950463#39950463) – Sergey Bespalov Oct 10 '16 at 02:17
1

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>
Sergey Bespalov
  • 1,746
  • 1
  • 12
  • 29