【问题标题】:Spring application doesn't appear to be persisting dataSpring 应用程序似乎没有持久化数据
【发布时间】:2013-08-25 04:37:22
【问题描述】:

我正在尝试向我的数据库中写入一些内容,但它无法正常工作,尽管它报告“成功完成请求”。成功后一切似乎都正常,我的控制器正确地重定向了我。

调试

DEBUG a.d.p.payment.PaymentServiceImpl - Requesting to persist new user'max_test@test.com'.
DEBUG a.d.p.payment.model.PaymentDAOImpl - Persisting com.app.payment.model.PaymentUser@86ceb985.
DEBUG o.s.o.j.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler - Creating new EntityManager for shared EntityManager invocation
DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13771737739
DEBUG o.h.e.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress
DEBUG o.s.o.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Invoking afterPropertiesSet() on bean with name 'redirectForm'
DEBUG o.s.web.servlet.DispatcherServlet - Rendering view [org.springframework.web.servlet.view.JstlView: name 'redirectForm'; URL [/WEB-INF/jsp/redirectForm.jsp]] in DispatcherServlet with name 'payment'
DEBUG o.s.web.servlet.view.JstlView - Forwarding to resource [/WEB-INF/jsp/redirectForm.jsp] in InternalResourceView 'redirectForm'
DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request

appContext.xml(根上下文)

<context:annotation-config />

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="username" value="test" />
    <property name="password" value="test" />
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <!--payment_test is the name of the schema -->
    <property name="url" value="jdbc:mysql://test1.com:3306/payment_test" /> 
</bean>

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="payment" />

    <property name="persistenceUnitManager">
        <bean class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager" >       
            <property name="defaultDataSource" ref="dataSource" />  
        </bean>
     </property>

    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="true" />
            <property name="generateDdl" value="true" />
            <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />

        </bean>
    </property>
</bean>

<context:component-scan base-package="com.app.payment" />
<context:annotation-config />

<!-- Auto scan the components -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" />

<tx:annotation-driven />

支付用户

@Entity
@Table(name="PaymentUser")
public class PaymentUser {

    @Id
    @GeneratedValue
    private int id;
    ...    
}

支付服务

@Transactional("transactionManager")
@Service()
public class PaymentServiceImpl implements PaymentService {

    @Autowired
    private PaymentDAO paymentDAO;
        // ... service methods
}

支付 DAO

@Repository()
public class PaymentDAOImpl implements PaymentDAO {

    //@PersistenceContext(unitName="payment")
    @PersistenceContext()
    EntityManager em;
}

似乎它甚至没有开始交易。希望这有足够的信息让有人帮助我。感谢您的帮助。

更新

获取数据工作正常。持久化 (EntityManager em.persist()) 和删除 (em.remove) 不起作用。会不会有正确的问题。意思是只有读权限,没有写权限,但在这种情况下,我认为应该有一个错误。

更新 2

&lt;aop:scoped-proxy /&gt; 添加到我的dataSource bean,但没有更改。就像我的调试信息说的那样

DEBUG o.h.e.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress

没有交易,但我的交易应该从哪里开始?

我还检查了问题Spring: Annotation-driven Transaction Manager 但我不知道该怎么做。

新的应用上下文

<?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:jee="http://www.springframework.org/schema/jee"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
        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-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:annotation-config />
    <tx:annotation-driven />

    <!-- Auto scan the components 
            <bean id="paymentDao" class="com.app.payment.model.PaymentDAOImpl" />
            <bean id="paymentService" class="com.app.payment.PaymentServiceImpl" />
    should do the same      
    -->
    <context:component-scan base-package="com.appn.payment" /> 

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


    <bean id="dataSource"
        class="org.apache.commons.dbcp.BasicDataSource" lazy-init="false" destroy-method="close"> 
        <aop:scoped-proxy />
        <property name="username" value="user" />
        <property name="password" value="pw" />
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://test1.com:3306/test" />
        <!--  <property name="testOnBorrow" value="true" />
        <property name="validationQuery" value="SELECT 1" />-->
    </bean>

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

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="payment" />
        <property name="dataSource" ref="dataSource" />
        <property name="persistenceUnitManager">
            <bean class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager" >       
                <property name="defaultDataSource" ref="dataSource" />  
            </bean>
         </property>

        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="generateDdl" value="true" />
                <!-- <property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />  -->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />

            </bean>
        </property>

        <property name="packagesToScan" value="com.app.payment" /> 
    </bean>

    <tx:annotation-driven />    

</beans>

更新 3

试图刷新我的 PaymentDAO em.flush(),这导致我看到错误消息。

javax.persistence.TransactionRequiredException: no transaction is in progress   at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:792)

这是:

public void flush() {
        if ( !isTransactionInProgress() ) {
            throw new TransactionRequiredException( "no transaction is in progress" );
        }
        try {
            getSession().flush();
        }
        catch ( RuntimeException e ) {
            throw convert( e );
        }
    }

我需要一些特别的会议吗? 也将其记录在我的控制器中

log.info("Is transaction active " + TransactionSynchronizationManager.isActualTransactionActive());

结果为假...不知道为什么没有活动交易...

更新 4

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@Repository
public class PaymentDAOImpl implements PaymentDAO {

    private final Logger log = LoggerFactory.getLogger(getClass());


    //@PersistenceContext()
    @PersistenceContext(unitName="payment")
    EntityManager em;

    @Override
    public void persist(PaymentUser user) {
        log.debug("Persisting {}.", user);
        em.persist(user);
        //em.flush();

    }

    @Override
    public void remove(PaymentUser user) {
        log.debug("Removing {}.", user);
        em.remove(user);
    }

    @Override
    public List<PaymentUser> getPaymentUsers() {
        log.debug("Fetching payment users.");
        return em.createQuery("FROM PaymentUser", PaymentUser.class).getResultList();
    }

    @Override
    public PaymentUser getPaymentUserById(String userId) {
        log.debug("Fetching payment users with id '{}'.",userId);
        return em.createQuery(
                "FROM PaymentUser WHERE userId = :userId", PaymentUser.class)
                .setParameter("userId", userId).getSingleResult();
    }

    @Override
    public void removePaymentUserById(String userId) {
        log.debug("Removing payment users with id '{}'.",userId);
        em.createQuery("DELETE FROM PaymentUser WHERE userId = :userId ", PaymentUser.class).
        setParameter("userId", userId).executeUpdate();

    }

    @Override
    public void mergePaymentUser(PaymentUser user) {
        log.debug("Merging payment user '{}'.",user);
        em.merge(user);
    }
}

更新 5

<?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">

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

        <!-- springapp servlet -->
    <servlet>
        <servlet-name>payment</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>payment</servlet-name>
          <url-pattern>/payment/*</url-pattern> 
          <url-pattern>/paymentExternalData</url-pattern>
        <url-pattern>/paymentInternalData</url-pattern> 
    </servlet-mapping>

    <!-- Welcome files -->
    <welcome-file-list>
         <welcome-file>payment.jsp</welcome-file>
         <welcome-file>payment.html</welcome-file>
    </welcome-file-list>

    <!-- S P R I N G -->

    <!-- Add Support for Spring -->
    <!-- Default applicationContext location: /WEB-INF/applicationContext.xml -->


    <!-- UTF-8 filter -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

更新 6

payment-servlet.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:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
        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-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:annotation-config />
    <tx:annotation-driven />

    <!-- Auto scan the components -->
    <context:component-scan base-package="com.app.payment" /> 

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

</beans>

appContext.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:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
        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-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:annotation-config />
    <tx:annotation-driven />

    <bean id="dataSource"
        class="org.apache.commons.dbcp.BasicDataSource" lazy-init="false" destroy-method="close"> 
        <aop:scoped-proxy />
        <property name="username" value="test" />
        <property name="password" value="test" />
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://test1.com/test" />
        <property name="testOnBorrow" value="true" />
        <property name="validationQuery" value="SELECT 1" />
    </bean>

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

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="payment" />
        <property name="dataSource" ref="dataSource" />
        <property name="persistenceUnitManager">
            <bean class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager" >       
                <property name="defaultDataSource" ref="dataSource" />  
            </bean>
         </property>

        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="generateDdl" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />

            </bean>
        </property>
    </bean>

</beans>

【问题讨论】:

  • 执行持久化的实际代码在哪里?
  • 向我们展示如何在控制器中获取服务实例。
  • PaymentController: @Autowired private PaymentService paymentService; PaymentDAO: public void persist(PaymentUser user) {em.persist(user)}
  • 刚刚尝试从我的数据库中获取现有的 PaymentUser,这非常好。所以只有坚持似乎不起作用。
  • 你能把dispatcher servlet使用的配置也贴出来吗?我的猜测是您正在复制 bean(服务)。

标签: java spring hibernate spring-mvc transactions


【解决方案1】:

确保在两个 xml 配置中都没有完全相同的 &lt;context:component-scan .../&gt; 元素。如果你有这个,你基本上是在复制你所有的 bean 实例。您最初拥有的是所有 bean 都由 ContextLoaderListener 加载,并且由于 &lt;tx:annotation-driven /&gt; 的存在而被代理。

现在,如果您的 payment-servlet.xml 中有相同的 &lt;context:component-scan .../&gt;,这将再次扫描所有创建另一个实例的 bean,但是由于没有 &lt;tx:annotation-driven /&gt;,它不会被代理并且没有应用交易。

现在发生的情况是,只要您需要其中一个 @Service 注释 bean,DispatcherServlet 就会首先在其自己的 ApplicationContext 中查找,以查看是否有 bean 可以满足其需求。如果有它将被使用(您当前的情况),如果没有,它将咨询父上下文(由ContextLoaderListener 加载的那个)。

您需要做的是配置ContextLoaderListener 以扫描所有 @Controller 带注释的bean 和DispatcherServlet 以扫描 @Controller带注释的豆子。这可以通过正确配置&lt;context:component-scan .../&gt;来完成。

applicationContext.xml

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

支付-servlet.xml

<context:component-scan base-package="com.appn.payment" use-default-filters="false">
  <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

这将为您提供事务和只有单个 bean 实例。您应该从 payment-servlet.xml 文件中删除 &lt;tx:annotation-driven /&gt;

仍有一个开放的JIRA issue 将其包含在参考指南中。 Spring Community Forums 中的一个线程也解释了这一点。

【讨论】:

    【解决方案2】:

    再次查看您的 applicationContext.xml,我注意到您没有使用您的 transactionManager 声明分配 entityManager。我不确定 Spring 是否会隐式设置它,但如果不是,它会解释为什么你的持久性不起作用。

    例如,改变:

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

    更新

    我只用容器管理的实体配置了 Spring+JPA(看起来也像你的意图)——从来没有真正的应用程序管理。据我所知,在使用 Spring 进行容器管理时,您实际上并不需要配置持久单元。我不能 100% 确定这是否会有所帮助,但请尝试更改您的 entityManagerFactory 在 applicationContext.xml 中的声明方式。

    <bean id="entityManagerFactory"
       class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    
       <property name="dataSource" ref="dataSource" />
    
       <property name="packagesToScan" value="com.app.payment" />   
    
       <property name="persistenceProvider">
          <bean class="org.hibernate.ejb.HibernatePersistence" />
       </property>
    
       <property name="jpaProperties">
          <props>
             <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
             <prop key="hiberate.show_sql">true</prop>
             ... //additional props
          </props> 
       </property>
    </bean>
    

    【讨论】:

    • 谢谢,但没有变化。 ... 你的意思是需要更多的属性吗?抱歉,我对这些东西有点陌生。
    • 我并没有任何额外的意思...这是误导 - 我将删除它,并添加一些额外的 cmets。
    • 嗯不工作。但我发现了不同的解决方案。可能是我有错误的代理? &lt;tx:annotation-driven/&gt;设置,应该是aspectj吗?!
    【解决方案3】:

    当使用注解驱动的事务时,你应该把@Transactional注解放在你使用数据库的方法之前。它可以是您的 DAO 或 Service 方法。我可以看到您已将 @Transactional 放在 PaymentServiceImpl 之上,但这不是该注释的正确位置。

    【讨论】:

    • @Transactionnal 注解在类的开头就足够了,你不需要把它放在每个方法之前,除非你想要一些特定的处理,例如可以更有效地处理的只读操作通过某些数据库访问框架。
    【解决方案4】:

    您似乎错过了 DAO 实现类开头的 @Transactional 注释。它将解释为什么您没有打开任何交易。

    如果还不足以解决你的问题,能否给我们你完整的DAO实现类?

    【讨论】:

      【解决方案5】:

      设法找到了问题。 Spring Docu "@EnableTransactionManagement 并且只在定义它们的同一应用程序上下文中的 bean 上查找 @Transactional。"在我的 payment-servlet.xml 中我没有 &lt;tx:annotation-driven /&gt; 这就是为什么没有活动交易。

      【讨论】:

      • 这意味着你在复制你的 bean。在您的 payment-servlet.xml 中,您应该只扫描带有 @Controller 注释的 bean。所有其他 bean 都存在于根应用程序上下文中(由 ContextLoaderListener 加载)。您现在拥有的是同一个 bean 的不同实例,这不是您想要的。 (因此我最初请求 payment-servlet.xml 以查看您是否确实有此问题)。请参阅我对代码的回答。
      • 查看我的其他答案,其中解释了如何防止这种情况(带有一些指向更多信息的链接)。
      【解决方案6】:

      创建 transactionManager bean 后,您应该在 Xml 文件中启用事务,

      如果你写了第二个,那么你必须在你的 DAO 类中使用@Transaction(),

      如果任何方法需要事务,那么在写入之前

      @Transactional(propagation=Propagation.REQUIRED, readOnly=false)

      它告诉事务是必需的,readOnly=false 表示您可以读取和写入数据。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-06
        • 1970-01-01
        相关资源
        最近更新 更多