tags:

views:

1759

answers:

1

I'm trying to create JUnit tests for my JPA DAO classes, using Spring 2.5.6 and JUnit 4.8.1.

My test case looks like this:


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:config/jpaDaoTestsConfig.xml"} )
public class MenuItem_Junit4_JPATest extends BaseJPATestCase {

    private ApplicationContext context;
    private InputStream dataInputStream;
    private IDataSet dataSet;

    @Resource
    private IMenuItemDao menuItemDao;

    @Test
    public void testFindAll() throws Exception {
        assertEquals(272, menuItemDao.findAll().size());
    }

    ... Other test methods ommitted for brevity ...
}

I have the following in my jpaDaoTestsConfig.xml:


<?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:p="http://www.springframework.org/schema/p"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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.xsd"&gt;

  <!-- uses the persistence unit defined in the META-INF/persistence.xml JPA configuration file -->
  <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="CONOPS_PU" /> 
  </bean>

    <bean id="groupDao" class="mil.navy.ndms.conops.common.dao.impl.jpa.GroupDao" lazy-init="true" />
    <bean id="permissionDao" class="mil.navy.ndms.conops.common.dao.impl.jpa.PermissionDao" lazy-init="true" />
    <bean id="applicationUserDao" class="mil.navy.ndms.conops.common.dao.impl.jpa.ApplicationUserDao" lazy-init="true" />
    <bean id="conopsUserDao" class="mil.navy.ndms.conops.common.dao.impl.jpa.ConopsUserDao" lazy-init="true" />

    <bean id="menuItemDao" class="mil.navy.ndms.conops.common.dao.impl.jpa.MenuItemDao" lazy-init="true" />

 <!-- enables interpretation of the @Required annotation to ensure that dependency injection actually occures -->
  <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>  

  <!-- enables interpretation of the @PersistenceUnit/@PersistenceContext annotations providing convenient
       access to EntityManagerFactory/EntityManager -->
  <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

  <!-- transaction manager for use with a single JPA EntityManagerFactory for transactional data access
       to a single datasource -->
  <bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
      <property name="entityManagerFactory" ref="entityManagerFactory"/>
  </bean>

  <!-- enables interpretation of the @Transactional annotation for declerative transaction managment
       using the specified JpaTransactionManager -->
  <tx:annotation-driven transaction-manager="jpaTransactionManager" proxy-target-class="false"/>

</beans>

Now, when I try to run this, I get the following:

SEVERE: Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@fa60fa6] to prepare test instance [null(mil.navy.ndms.conops.common.dao.impl.MenuItem_Junit4_JPATest)]
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mil.navy.ndms.conops.common.dao.impl.MenuItem_Junit4_JPATest': Injection of resource fields failed; nested exception is java.lang.IllegalStateException: Specified field type [interface javax.persistence.EntityManagerFactory] is incompatible with resource type [javax.persistence.EntityManager]
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessAfterInstantiation(CommonAnnotationBeanPostProcessor.java:292)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:959)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:329)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:110)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:255)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:93)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:130)
    at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:61)
    at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:54)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
    at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:52)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: java.lang.IllegalStateException: Specified field type [interface javax.persistence.EntityManagerFactory] is incompatible with resource type [javax.persistence.EntityManager]
    at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.checkResourceType(InjectionMetadata.java:159)
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.(PersistenceAnnotationBeanPostProcessor.java:559)
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$1.doWith(PersistenceAnnotationBeanPostProcessor.java:359)
    at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:492)
    at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:469)
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findPersistenceMetadata(PersistenceAnnotationBeanPostProcessor.java:351)
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(PersistenceAnnotationBeanPostProcessor.java:296)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors(AbstractAutowireCapableBeanFactory.java:745)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:448)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
    at java.security.AccessController.doPrivileged(AccessController.java:219)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:221)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:168)
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:435)
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:409)
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:537)
    at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:180)
    at org.springframework.beans.factory.annotation.InjectionMetadata.injectFields(InjectionMetadata.java:105)
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessAfterInstantiation(CommonAnnotationBeanPostProcessor.java:289)
    ... 18 more

It seems to be telling me that its attempting to store an EntityManager object into an EntityManagerFactory field, but I don't understand how or why. My DAO classes accept both an EntityManager and EntityManagerFactory via the @PersistenceContext attribute, and they work find if I load them up and run them without the @ContextConfiguration attribute (i.e. if I just use the XmlApplcationContext to load the DAO and the EntityManagerFactory directly in setUp ()).

Any insights would be appreciated.

Thanks.

--Steve

+3  A: 

These are the correct combinations of annotation + interface:

@PersistenceContext
private EntityManager entityManager;


@PersistenceUnit
private EntityManagerFactory entityManagerFactory;

But when using spring's transaction and entity manager support, you don't need the EntityManagerFactory at all.

The reason why you don't need EntityManagerFactory is because the creation of the EntityManager is responsibility of the transaction manager. Here's what happens in short:

  • the transaction manager is triggered before your methods
  • the transaction manager gets the EntityManagerFactory (it is injected in it), creates a new EntityManager, sets in in a ThreadLocal, and starts a new transaction.
  • then it delegates to the service method
  • whenever @PersistenceContext is encountered, a proxy is injected (in your Dao), which, whenever accessed, gets the current EntityManager which has been set in the ThreadLocal
Bozho
You still need both the EntityManager and Factory in the DAOs, which is the only place they exist in my code.
Steve
no, you don't. For example my entire application has no access to the factory whatsoever. The EntityManager is injected by spring, so no need to use the factory manually.
Bozho
Right. We're doing the same thing with the DAOs (injecting the EntityManager). I'm also injecting the Factory (although I'm not currently using it for anything). It turns out that the whole problem was a simple cut-and-paste error. I was using @PersistenceContext on the EntityManagerFactory, rather than @PersistenceUnit. Once I changed that it worked... Thanks for the pointer...
Steve
@Steve - accepted answer with 0 upvotes seems strange, so I'd suggest your practice being the regular - accept + upvote
Bozho
@Bozho - IMO this answer is not complete, since a clear spring config should be provided for @PersistenceContext private EntityManager entityManager; example. Why group the EntityManager and EntityManagerFactory in the same block when they should never appear in the same class together.
emeraldjava