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>
We can inject an EntityManager object in a Spring Bean such as repository, service or controller… using @Autowired annotation. @Repository public class TutorialRepository { @Autowired private EntityManager entityManager; } Spring Data JPA will initialize EntityManagerFactory for default persistence unit at runtime.
Once the EntityManagerFactory is closed or application shutdowns, all its EntityManager s are closed. How to access JPA Entity Manager? We can inject an EntityManager object in a Spring Bean such as repository, service or controller… using @Autowired annotation.
EntityManager is used to interact with persistence context and EntityManagerFactory interacts with entity manager factory. Using EntityManager methods, we can interact with database.
This example shows use of @PersistenceContext to have an EntityManager with an EXTENDED persistence context injected into a @Stateful bean. A JPA @Entity bean is used with the EntityManager to create, persist and merge data to a database. The entity itself is simply a pojo annotated with @Entity.
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!!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With