3

I have a Spring MVC project that is reading/writing to a MySQL database.

Here's my DAO ... the getAll() and getLabByLabId(...) work fine.

When I do a saveLab(...) I get this exception on em.persist(lab) ...

javax.persistence.TransactionRequiredException: No transactional EntityManager available
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:275)
    at com.sun.proxy.$Proxy52.persist(Unknown Source)
    at com.mycompany.ion.labutil.dao.LabDAOImpl.saveLab(LabDAOImpl.java:29)
    at com.mycompany.ion.labutil.service.LabServiceImpl.saveLab(LabServiceImpl.java:52)
    at com.mycompany.ion.labutil.controller.LabController.doSaveLab(LabController.java:110)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:777)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:706)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:617)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:279)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

Here's the DAO ...

package com.mycompany.ion.labutil.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

import org.springframework.stereotype.Repository;

import com.mycompany.ion.labutil.domain.Lab;

@Repository
public class LabDAOImpl implements LabDAO {

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

    public List<Lab> getAll() throws Exception {
        return em.createQuery("FROM Lab").getResultList();
    }

    public Lab getLabByLabId(String labId) throws Exception {
        return em.find(Lab.class, labId);
    }

    @Transactional
    public Lab saveLab(Lab lab) throws Exception {
        em.persist(lab);
        return getLabByLabId(lab.getLab_id());
    }


}

Here's my persistence.xml:

<persistence 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"
    version="1.0">
    <persistence-unit name="JpaPersistenceUnit"
        transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
    </persistence-unit>
</persistence>

And here's /webapp/WEB-INF/spring/root-context.xml ...

<?xml version="1.0" encoding="UTF-8"?>

<!-- BEGIN: original header 
<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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
     END: original header -->

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">


    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3308/labutil1"
        p:username="root" p:password="password" p:initialSize="5" p:maxActive="10">
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="packagesToScan" value="com.mycompany.ion.labutil.dao" />
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="generateDdl" value="false" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
            </bean>
        </property>        
    </bean>

    <context:component-scan base-package="com.mycompany.ion.labutil.dao">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Repository" />
    </context:component-scan>

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

    <tx:annotation-driven transaction-manager="transactionManager" />

    <context:spring-configured />
    <context:annotation-config />
</beans>

Here's /webapp/WEB-INF/spring/appservlet/servlet-context.xml ...

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->

    <!-- Enables the Spring MVC @Controller programming model -->
    <annotation-driven />

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />

    <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>

    <context:component-scan base-package="com.mycompany.ion" />



</beans:beans>

.

.

.

SOLUTION DETAILS:

Here's the solution that worked.

I changed root-context.xml to this:

<context:component-scan base-package="com.mycompany.myteam.myapp" >
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

And I changed servlet-context.xml to this:

<context:component-scan base-package="com.mycompany.myteam.myapp.mycontrollers" /> 

Thanks malejpavouk !!

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
  • 1
    Let me guess... You have another `*context.xml` which also contains a `context:component-scan`... Please add the **full** stack trace and not a snippet mostly the interesting information is at the end of the stack trace and not the top. – M. Deinum Mar 07 '17 at 06:25
  • @M.Deinum Hi, I added the full stacktrace above, and yes, looks like there are two *context.xml files ... I included them both above `/webapp/WEB-INF/spring/root-context.xml` .. and `/webapp/WEB-INF/spring/appservlet/servlet-context.xml` ... and yes there are two context:component-scan sections. You're good at this! What do I do to fix it? –  Mar 07 '17 at 14:30
  • 1
    Doing ~14 years of spring development helps :). Disable the default filters in your `servlet-context.xml` and only scan for `@Controllers` using an include filter. (You might want to exclude scanning for `@Controllers` in your root context). – M. Deinum Mar 07 '17 at 15:15
  • @M.Deinum Thx for the help. I need more hand-holding. So I (a) remove the `context:component-scan` section from `servlet-context`; and (b) add correct `include` to the `context:component-scan` in `root-context`. Is that right? Not sure how to do part (b) ... do you have an example? –  Mar 07 '17 at 16:30
  • No you leave both, 1 scans everything BUT `@Controller` and the other scans only for `@Controller`. – M. Deinum Mar 08 '17 at 06:50
  • in root exclude () all controllers, in dispatcher scan only controllers. See the context mechanics in my answer – malejpavouk Mar 21 '17 at 21:48

1 Answers1

1

2 different contexts. Root is the parent context, dispatcher is the child one. Child (web) context should contain only beans for the perimeter/www (Controllers). The mechanics is that child context sees everything from parent context, but the parent context does see beans from child.)

You component scan the whole application in the child context. So my guess will be that tx:annotation-driven will only instrument beans, which are directly contained in the given context (and not those, which are just visible). So do 2 scans, one for controllers in child/dispatcher context, the second for all common beans in the parent/root.

malejpavouk
  • 4,297
  • 6
  • 41
  • 67
  • Thanks for the help! At long last I got back to this issue and fixed it with your advice. –  Jun 20 '17 at 20:11