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>
Advertisement
Answer
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!!