Search code examples
javaspringclasscastexceptionapplicationcontext

How to retrieve Spring's entity manager factory?


I'm trying to write some JUnit tests for a Spring 2.0.7 application.

In my application context XML file I have this bean:

<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" 
      id="entityManagerFactory"> ... </bean>

In my DAOs, I just annotate the setter for the entity manager and Spring does all the magic:

@PersistenceContext
public void setEntityManager(EntityManager em) {
    this.em = em;
}

In Spring 2.5+, I would just annotate my test class with @RunWith(SpringJUnit4ClassRunner.class) and @ContextConfiguration(locations = {"/applicationContext-test.xml"}). But my Spring version is too old, there isn't a SpringJUnit4ClassRunner. Therefore, no Spring magic for my JUnit tests. Instead, I'm doing it all myself:

// Load the XML file myself
XmlWebApplicationContext appContext = new XmlWebApplicationContext();
appContext.setConfigLocations(new String[] { "classpath:applicationContext-test.xml" });
appContext.refresh();

// Retrieve the entity manager factory
Object obj = appContext.getBean("entityManagerFactory");
// ***** The following line throws an exception: *****
LocalContainerEntityManagerFactoryBean aux = (LocalContainerEntityManagerFactoryBean) obj;
EntityManagerFactory emf = aux.getObject();

// Instantiate the EM and inject it into the DAOs
EntityManager em = emf.createEntityManager();
myDAO.setEntityManager(em);

I get a ClassCastException: Cannot cast $Proxy23 ... exception when I try to cast obj to aux. However, if I set a breakpoint in Eclipse and inspect obj, it actually is an instance of LocalContainerEntityManagerFactoryBean.

I have tried this approach which I've seen in quite a few questions here. However, AopUtils.isJdkDynamicProxy(obj) returns false. Furthermore, even trying to cast (Advised) obj throws the same exception.

I have also tried casting obj to FactoryBean, which is one of the interfaces implemented by LocalContainerEntityManagerFactoryBean... and yes, it throws the same exception. Which is confusing, because almost every answer to "class cast exception proxy" say that "you can only cast to interfaces, not to classes".

So, my questions are:

  • Is there any way to fix the code I posted? (Yes, I wish I could upgrade Spring! But no, I can't).
  • OR: is there any other way in which I can instantiate the entity manager using the EMF defined in the XML file? Please note that I can't do just this:
emf = Persistence.createEntityManagerFactory("myPersistenceUnit");
em = emf.createEntityManager();

That works fine, but I lose the <aop:config> and <tx:advice> entries defined in the applicationContext-test.xml file.


Solution

  • You can use another version of the getBean(...) method to define the the required type more strictly, see official Spring 2.0 JavaDoc:

    public Object getBean(String name, Class requiredType)
    

    which states:

    requiredType - type the bean must match. Can be an interface or superclass of the actual class, or null for any match. For example, if the value is Object.class, this method will succeed whatever the class of the returned instance.

    Thus, to solve your problem with casts, you need to implement it this way:

    // Retrieve the entity manager factory
    EntityManagerFactory emf = (EntityManagerFactory) 
       appContext.getBean("entityManagerFactory", javax.persistence.EntityManagerFactory.class);
    

    With this, you will directly retrieve a "neutral" EMF, compatible with the interface as described by the JPA specification. At runtime, this will be a proxy object, yet you can use it the same way as you originally intended:

    // Instantiate the EM and inject it into the DAOs
    EntityManager em = emf.createEntityManager();
    // ... whatever comes here - most likely: doing some CRUD operations...
    

    Hope it helps.