views:

1332

answers:

4

In the following code I am trouble with my injected EnitityManager, which always shows up as null;

public class GenericController extends AbstractController {

    @PersistenceContext(unitName = "GenericPU")
    private EntityManager em;

    protected ModelAndView handleRequestInternal(
            HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        //(em == null) is always trigged
        if (em == null) throw new NullPointerException("em is null");
        Collection<Generic> Generics = em.createNamedQuery("Generic.findAll").getResultList();

        ModelAndView mav = new ModelAndView("Generic");
        mav.addObject(Generics); 
        return mav;
    }
}

Here is the bean definition, defined in dispatcher-servlet.xml.

<bean id="Generic" class="com.application.web.GenericController" />

EnitityManager should be injected based on tx:annotation-based, defined in the persistence-context.xml file.

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
        <property name="url" value="removed" />
        <property name="username" value="removed" />
        <property name="password" value="removed" />
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="GenericPU" />
        <property name="dataSource" ref="dataSource" />
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
        </property>
        <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.SQLServerDialect" />
            </bean>
        </property>
    </bean>

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

    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

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

</beans>

The persistence-context is loaded from the applicationContext.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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;    
    <import resource="classpath:META-INF/persistence-context.xml"/>
</beans>

The classpath import is done because the ORM entities are included as a JAR file into the project. Note that I believe the persistence-context is being loaded, as Spring will not allow the application to be deployed if it can't parse its configuration files.

Here is my persistence.xml.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"&gt;
  <persistence-unit name="CoolOrmJpaPU" transaction-type="RESOURCE_LOCAL">
    <class>com.application.orm.jpa.Generic</class>
    <!-- bunch more classes -->
  </persistence-unit>
</persistence>

And my web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"&gt;
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>*.htm</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>redirect.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Can anyone help me out here?

A: 

I think you need a file persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
    xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"&gt;

  <persistence-unit name="GenericPU">
     <class>com.domain.MyClass</class>
  </persistence-unit>

</persistence>

I think it will not work if the file has a different name, especially not since you don't tell the EntityManager factory the new name anywhere.

Aaron Digulla
I do have a persistence.xml, I left it out because I didn't think if would be critical to the issue.
James McMahon
Sorry about that I've added it to the question.
James McMahon
A: 

Hi,

I used to work with an older spring version, when you had to put setProperty() to the bean and set the propery tag inside the spring-bean definition like:

<bean id="Generic" class="com.application.web.GenericController" />
    <property name="em" ref="entityManager" />
</bean>

Maybe you should work with the transactionManager or the entityManagerFactory beans...

PD: I personally prefer the old way of injecting dependencies.

ATorras
A: 

Have you included

<context:annotation-config />

In your spring XML. Reference here

toolkit
I don't have that, but I believe it is turned on implicitly by setting tx:annotation-driven.
James McMahon
+3  A: 

I was lucky enough today to be able to speak with a consultant about this issue, he was able to help me sort the whole thing out.

So my problem is that Spring MVC was establishing two distinct contexts, one application context, defined in applicationContext.xml and one web context, defined in dispatcher-servlet.xml.

Beans from one context can not talk to beans in another context, thus when I initilized my persistence context inside of applicationContext.xml, I could not access it in beans loaded by dispatcher-servlet.xml, ie my controllers.

When Netbeans auto-generated the base to my Spring MVC it set this up by default. In some large web applications, it would make sense to separate the web part of the application in a context distinct from the rest of the logic (persistence, business code, etc). In my case, where I am trying to auto inject my entity manager directly into my controllers, this worked against me.

To fix the issue I moved the line

<import resource="classpath:META-INF/persistence-context.xml"/>

From the applicationContext.xml, to my dispatcher-servlet.xml. My controllers then were properly injected with EntityManagers from the @PersistanceContext annotation.

James McMahon
The whole process of XML configuration for the web layer is described in the Spring reference manual, chapter 13: http://static.springframework.org/spring/docs/2.5.x/reference/mvc.html
MetroidFan2002
Er, sorry, not just XML configuration but configuration in general - where is the edit comment button already?!
MetroidFan2002
So how did you solve it? As I understand it - the ApplicationContext serves as the "root" or "parent" context to the dispatcherServlet context (and all other contexts), so all beans defined in the application context should be available to it. e.g. Controllers generally have other @Service or @Component beans injected into them, which can be defined in the applicationContext. So your answer doesn't really explain why it wasn't working for you.
Daniel Alexiuc
@justSomeJavaGuy, I've added some more detail to the answer. To answer you directly, from the way it was explained to me was that the application context and the web context (the dispatcher servlet) exist in two distinct areas of memory and are unable to communicate with each other. This segmentation is specific to the way my application is configured in the web.xml file and potentially you could set it up so only one Spring context existed.
James McMahon
Note, that last sentence is pure speculation on my part. I am new to the Spring framework and am far from a being an expert.
James McMahon
In my graphic, the "controller" label for both context is confusing and incorrect. It should be Web Context/Application Context. I will fix that up in the future if I get time.
James McMahon
Thanks for the update - I thinks it's just confusion over the word 'bean' in your diagram. Spring 'beans' should be available in both contexts, but perhaps the units defined in your persistence.xml don't qualify as managed Spring 'beans' and therefore didn't propagate to the child (dispatcher-servlet.xml) context. I'm speculating a bit too but that's the way I understood it.
Daniel Alexiuc
You may be right there, again this was explained to me, so I'm not sure 100%.
James McMahon
I will try to update this answer when I have time but just as a quick update, the information here is misleading. The web context CAN talk to the application context, but not vice versa.
James McMahon
I'm having the same problem as the original asker and this did not solve it for me.
Trampas Kirk