2

您好我面临以下问题:我有一个带有以下配置文件的 spring mvc 应用程序。有两个单独的 spring 配置文件,一个用于 jpa,一个用于 spring mvc 问题是当我尝试在数据库中保留某些内容时出现错误:

消息请求处理失败;嵌套异常是 javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - 无法可靠地处理“持久”调用

(我在服务类上有@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_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>

和 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/>

道:

@Repository
public class GoalRepositoryImpl implements  GoalRepository {

  @PersistenceContext
  private EntityManager em;

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

      return goal;
  }
}
4

1 回答 1

1

您好最后我在同事的帮助下找到了解决方案。

Spring 一次可以有多个上下文。

  • 其中一个将是根上下文,所有其他上下文将是子上下文。
  • 所有子上下文都可以访问根上下文中定义的 bean,但不能相反。

在我的 web.xml 中,我有两个配置 xml 文件 servlet-config.xml 和 jpaContext.xml 第一个由创建子应用程序上下文的 DispacherServlet 使用。第二个由创建根应用程序上下文的 ContextLoaderListener 使用。

我在子上下文中有组件扫描元素,因此在子上下文中创建了 bean。当服务 bean 尝试使用注释开始新事务时,注释驱动无法看到 bean(因为它来自子上下文),因此我收到了错误。

通过更改 servlet-config.xml 中的组件扫描以仅创建控制器:

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

并将新组件扫描添加到根上下文(jpaContext.xml)

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

问题解决了。

于 2020-02-21T11:00:56.427 回答