Нет EntityManager с фактической транзакцией, доступной для текущего потока: несколько файлов конфигурации xml

Здравствуйте, я столкнулся со следующей проблемой: у меня есть приложение Spring mvc со следующими файлами конфигурации. есть два отдельных файла конфигурации Spring: один для jpa и один для spring mvc. Проблема в том, что когда я пытаюсь сохранить что-то в базе данных, я получаю сообщение об ошибке:

Ошибка обработки запроса сообщения; вложенное исключение - javax.persistence.TransactionRequiredException: нет EntityManager с фактической транзакцией, доступной для текущего потока - не может надежно обработать вызов 'persist'

(У меня есть @Transactional в классе обслуживания), если я перемещаю из jpaConfig.xml в servlet-config.xml, приложение работает нормально. Я не могу понять, почему это происходит, и считаю неправильным переносить этот тег в файл конфигурации mvc. Вы можете помочь мне понять, почему это происходит?

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
@Repository
public class GoalRepositoryImpl implements  GoalRepository {

  @PersistenceContext
  private EntityManager em;

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

      return goal;
  }
}
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;
  }
}

person Kasiaras Dimitrios    schedule 14.02.2020    source источник
comment
Вы сказали, что @Transactional находится в классе обслуживания? Есть ли @EnableTransactionManagement в вашей конфигурации? См. здесь: Однако простого присутствия аннотации @Transactional недостаточно для активации транзакционного поведения и этого ответа   -  person crusy    schedule 14.02.2020
comment
Так как я использую xml-конфигурацию, эквивалентом @EnableTransactionManagment является ‹tx: anotation-driven transaction-manager = txManager /› Проблема в том, что у меня есть два config.xml, один для jpa-config и один для servlet-config, когда я помещаю ‹Tx: annotation-driven /› в конфигурации сервлета он работает нормально, но когда я помещаю его в jpa-Config, я получаю сообщение об ошибке.   -  person Kasiaras Dimitrios    schedule 14.02.2020
comment
Попробуйте указать id: <tx:annotation-driven transaction-manager="transactionManager" /> в вашем jpaContext.xml   -  person crusy    schedule 14.02.2020
comment
С атрибутом transaction-manager = transactionManager у него такая же ошибка, единственный способ работать правильно - переместить его в другой servlet-config.xml   -  person Kasiaras Dimitrios    schedule 14.02.2020


Ответы (1)


Здравствуйте. Наконец-то я нашел решение с помощью коллеги.

Spring может иметь несколько контекстов одновременно.

  • Один из них будет корневым контекстом, а все остальные контексты будут дочерними контекстами.
  • Все дочерние контексты могут иметь доступ к bean-компонентам, определенным в корневом контексте, но не наоборот.

В моем web.xml у меня есть два файла конфигурации xml: servlet-config.xml и jpaContext.xml, первый из которых используется DispacherServlet, который создает контекст дочернего приложения. второй используется ContextLoaderListener, который создает контекст корневого приложения.

У меня был элемент сканирования компонентов в дочернем контексте, поэтому bean-компоненты были созданы внутри дочернего контекста. Когда служебный компонент пытался начать новую транзакцию с аннотацией, управляемый аннотацией объект не мог видеть объект (потому что он был из дочернего контекста), и поэтому я получал сообщение об ошибке.

изменив сканирование компонентов в servlet-config.xml, чтобы создать только контроллеры:

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

и добавьте сканирование нового компонента в корневой контекст (jpaContext.xml)

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

проблема решена.

person Kasiaras Dimitrios    schedule 21.02.2020