7

I writing tests for my DAO classes using JPA, with Hibernate as JPA provider, and Spring 3.2. I am not able to inject the entity manager correctly, I get a NullPointerException when trying to access it. My GenericDAO implementation looks like this:

@Repository
public class GenericDAOImpl implements GenericDAO {

    @PersistenceContext(unitName="unitname")
    private EntityManager entityManager;

    public EntityManager getEntityManager() {
        return entityManager;
    }


    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    // NullPointerException when calling this, entityManager is null
    public Query createNamedQuery(String name) {
        return entityManager.createNamedQuery(name);
    }        

    // many other methods....
}

The class of the test looks like this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/com/main/resources/root-context.xml", "/com/main/resources/servlet-context.xml"})
public class TestModel {

    @Before
    public void setUp() throws Exception{
        ...
    }

    @Test
    public void test(){
        ...
    }
}

My root-context.xml is the following:

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd        
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        "
>

    <!-- Root Context: defines shared resources visible to all other web components -->

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="unitname" />
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="com.main" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="generateDdl" value="false" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
            </bean>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />



    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false" />
    <context:component-scan base-package="com.main" />
    <context:annotation-config />

</beans>

I've tried several approaches without success, even adding the PersistenceAnnotationBeanPostProcessor as suggested in other SO questions. All other things seem to work fine: Hibernate creates the database tables, the context is loaded, etc. What I am doing wrong?

Edit: the stack trace is the following:

java.lang.NullPointerException
    at com.main.model.dao.JPAImpl.GenericDAOImpl.createNamedQuery(GenericDAOImpl.java:119)
    at com.main.model.bo.DescriptorBO.persist(DescriptorBO.java:52)
    at com.main.webmodule.JSONSerializer.deserialize(JSONSerializer.java:149)
    at com.main.tests.TestModel.test(TestModel.java:86)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

And here is the persistence.xml:

<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="unitname" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <mapping-file>META-INF/jpql/NamedQueries.xml</mapping-file>


        <class>com.main.model.Account</class>
        <class>com.main.model.Action</class>
        <class>com.main.model.Device</class>
        <class>com.main.model.DeviceDescriptor</class>
        <class>com.main.model.Event</class>
        <class>com.main.model.File</class>
        <class>com.main.model.Rule</class>
        <class>com.main.model.StateVar</class>
        <class>com.main.model.Argument</class>
        <exclude-unlisted-classes>false</exclude-unlisted-classes> 


        <properties>

            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.connection.password" value="root"/>
            <property name="hibernate.connection.provider_class" value="org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider" />
            <property name="hibernate.connection.autocommit" value="true"/>
            <property name="hibernate.connection.release_mode" value="auto"/>
            <property name="hibernate.connection.url" value="jdbc:mysql://127.0.0.1:3306/testweb"/>
            <property name="hibernate.hbm2ddl.auto" value="create"/>

            <property name="hibernate.c3p0.min_size" value="1" />
            <property name="hibernate.c3p0.max_size" value="10" />
            <property name="hibernate.c3p0.acquire_increment" value="1" />
            <property name="hibernate.c3p0.idle_test_period" value="300" />
            <property name="hibernate.c3p0.max_statements" value="0" />
            <property name="hibernate.c3p0.timeout" value="100" />

            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>

        </properties>




    </persistence-unit>
</persistence>
joanlofe
  • 3,360
  • 2
  • 26
  • 44
  • Post your stacktrace. – Vineet Singla May 07 '13 at 10:23
  • Show us your `persistence.xml` – falsarella May 07 '13 at 11:58
  • Do you have the `root-context.xml` and `ContextLoaderListener` configured in your web.xml?see http://stackoverflow.com/q/9856721/1064325 and http://stackoverflow.com/q/1132565/1064325 – falsarella May 07 '13 at 12:33
  • I had the same problem and to this day I did not find a solution, I found a workaround tho `entityManager = Persistence.createEntityManagerFactory("unitname").createEntityManager();` instead of `@PersistenceContext(unitName="unitname") private EntityManager entityManager;` I think it has smth to do with annotations. If you find a solution please post it – cosmin.danisor May 07 '13 at 12:50

2 Answers2

10

I've finally managed to solve the problem. To instantiate GenericDAO I used an autowired annotation, this way:

@Autowired
private GenericDAO genericDao;

but the class where this happens, called DescriptorBO, was instantiated this way:

DescriptorBO descrBO = new DescriptorBO(...);

and thus escaped completely from the control of the Spring container. Changing this into:

@Autowired
private DescriptorBO descrBO;

and adding the appropriate bean definitions to the root-context.xml:

<bean name="descriptorBO" class="com.main.model.bo.DescriptorBO">
    <property name="genericDao" ref="genericDao" />
</bean>

<bean name="genericDao" class="com.main.model.dao.JPAImpl.GenericDAOImpl" /> 

solved the problem. Now the EntityManager is injected properly.

Lesson learnt: if Spring does not inject the EntityManager (or any other injected object) check that all the object hierarchy above your object is managed by Spring, i. e. instantiated from beans in the application context, either directly or using the Autowired annotation. Check that you do not use the new operator to instantiate any of those objects!!

joanlofe
  • 3,360
  • 2
  • 26
  • 44
0

If somebody will came here for a right answer:

To autowire EntityManger in you Spring bean without calling EntityManagerFactory.createEntityManager(), you need to add the PersistenceAnnotationBeanPostProcessor bean to your configuration:

....
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/data/jpa
                    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"
...

<jpa:repositories base-package="com.galera" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
 
...

<!-- Let's forget about Maven -->   

<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-jpa</artifactId>
</dependency>

And after that the Autowiring for EntityManager would work fine!

@Autowire
EntityManager entityManager; // Magic (*_*)
sergpank
  • 988
  • 10
  • 18