Kasiaras Dimitrios
Kasiaras Dimitrios

Reputation: 261

No EntityManager with actual transaction available for current thread : Multiple config xml files

Hello I face the following problem: I Have a spring mvc app with the following configuration files. there are two separate spring config files one for jpa and one for spring mvc The problem is when I try to persist something in Database I get the Error:

Message Request processing failed; nested exception is javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call

(I have the @Transactional on the service class) if I move the from jpaConfig.xml to servlet-config.xml the application works fine. I cannot find out why this happens and I find it wrong to move this tag to mvc config file. Can you help me to understand why this is happening?

Web.xml:

<?xml version="1.0" encoding="UTF-8"?>
     <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
     version="4.0">

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:jpaContext.xml</param-value>
</context-param>


<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>fitTrackerServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/config/servlet-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>fitTrackerServlet</servlet-name>
    <url-pattern>/app/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>fitTrackerServlet</servlet-name>
    <url-pattern>/pdfs/**</url-pattern>
</servlet-mapping>

servlet-config.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"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xsi:schemaLocation="
   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
   http://www.springframework.org/schema/mvc 
  http://www.springframework.org/schema/mvc/spring-mvc.xsd">

<mvc:annotation-driven/>
<context:component-scan base-package="com.example"/>

<mvc:interceptors>
    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
        <property name="paramName" value="language"/>
    </bean>
</mvc:interceptors>

<mvc:resources mapping="pdfs" location="/pdfs/**"/>

<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver" >
    <property name="order" value="1" />
    <property name="contentNegotiationManager" >
        <bean class="org.springframework.web.accept.ContentNegotiationManager">
            <constructor-arg>
                <bean class="org.springframework.web.accept.PathExtensionContentNegotiationStrategy" >
                    <constructor-arg>
                        <map>
                            <entry key="json" value="application/json" />
                            <entry key="xml" value="application/xml" />
                        </map>
                    </constructor-arg>
                </bean>
            </constructor-arg>
        </bean>
    </property>
    <property name="defaultViews">
        <list>
            <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" />
            <bean class="org.springframework.web.servlet.view.xml.MarshallingView" >
                <constructor-arg>
                    <bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
                        <property name="classesToBeBound">
                            <list>
                                <value>com.example.domain.Activity</value>
                            </list>
                        </property>
                    </bean>
                </constructor-arg>
            </bean>
        </list>
    </property>
</bean>

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
    <property name="order" value="2" />
</bean>

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="messages"/>
</bean>

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
    <property name="defaultLocale" value="en"/>
</bean>

and the jpaContext.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: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">

<bean id="entityManagerFactoryBean" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="packagesToScan" value="com.example.domain"/>

    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.hbm2ddl.auto">create</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
        </props>
    </property>
</bean>

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/fitness_tracker?autoReconnect=true"/>
    <property name="username" value="root"/>
    <property name="password" value="password"/>
</bean>

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

<tx:annotation-driven/>

Dao:

@Repository
public class GoalRepositoryImpl implements  GoalRepository {

  @PersistenceContext
  private EntityManager em;

  @Override
  @Transactional
  public Goal save(Goal goal) {
      em.persist(goal);
      em.flush();

      return goal;
  }
}

Upvotes: 2

Views: 205

Answers (1)

Kasiaras Dimitrios
Kasiaras Dimitrios

Reputation: 261

Hello Finally I found the solution with the help of a colleague.

Spring can have multiple contexts at a time.

  • One of them will be the root context, and all the other contexts will be child contexts.
  • All child contexts can access the beans defined in root context, but not the opposite.

In my web.xml I have two configuratation xml files the servlet-config.xml and the jpaContext.xml the first one is used by the DispacherServlet which creates a child application context. the second is used by the ContextLoaderListener which creates the root application context.

I had the component-scan element in the child context so the beans were created inside the child context. When the service bean was trying to begin a new transaction with the annotation the annotation-driven could not see the bean (because it was from the child context) and thus I was getting the error.

by changing the component scan in servlet-config.xml to create only the controllers :

<context:component-scan base-package="com.example.controllers"/>

and addin a new component scan to the root context (jpaContext.xml)

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

the problem solved.

Upvotes: 1

Related Questions