简体   繁体   English

Spring应用程序似乎不是持久化数据

[英]Spring application doesn't appear to be persisting data

I'm trying to write something into my database but it's not working despite it reporting "Successfully completed request". 我正在尝试将一些内容写入我的数据库,但尽管报告“已成功完成请求”,但它仍无法正常工作。 After the success everything seems to work fine and my controller redirects me correctly. 成功之后,一切似乎都运行良好,我的控制器正确地重定向我。

Debug 调试

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 (root context) 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 />

PaymentUser PaymentUser

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

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

PaymentService PaymentService

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

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

Payment DAO 付款DAO

@Repository()
public class PaymentDAOImpl implements PaymentDAO {

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

It seems like it doesn't even start a transaction. 似乎它甚至没有开始交易。 Hope thats enough info for somebody to help me. 希望有足够的信息让某人帮助我。 Thanks for any help. 谢谢你的帮助。

UPDATE UPDATE

Getting data works fine. 获取数据工作正常。 Persisting (EntityManager em.persist() ) and removing ( em.remove ) are not working. 持久化(EntityManager em.persist() )和删除( em.remove )不起作用。 Could there be a right problem. 可能有正确的问题。 Meaning just read rights and no write rights, but in this case there should be an error i thought. 意思是只读权限而没有写权限,但在这种情况下我应该有一个错误。

UPDATE 2 更新2

Added <aop:scoped-proxy /> to my dataSource bean, but no changes. <aop:scoped-proxy />到我的dataSource bean,但没有更改。 Like my debug msg said 就像我的调试消息说的那样

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

There is no transaction, but where should my transaction start? 没有交易,但我的交易应该从哪里开始?

I also checked issues Spring: Annotation-driven Transaction Manager but I'm not sure what to do. 我还检查了问题Spring:Annotation-driven Transaction Manager但我不知道该怎么做。

New appContext 新的appContext

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

UPDATE 3 更新3

Tried to flush in my PaymentDAO em.flush() which leads me to the error message. 试图在我的PaymentDAO em.flush()刷新,这会导致我收到错误消息。

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

which is: 这是:

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

Do i need some kind of special session ? 我需要某种特殊会话吗? Also logged it in my controller 还记录在我的控制器中

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

which results to false... no sure why there is no active transaction... 这导致错误...不确定为什么没有活动交易......

UPDATE 4 更新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);
    }
}

UPDATE 5 更新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>

UPDATE 6 更新6

payment-servlet.xml 支付-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 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>

Make sure that you don't have exact duplicate <context:component-scan .../> elements in both xml configurations. 确保在两个xml配置中没有完全重复的<context:component-scan .../>元素。 If you have this you are basically duplicating all your bean instances. 如果你有这个,你基本上复制了所有的bean实例。 What you initially had was all beans get loaded by the ContextLoaderListener and those are proxied due to the existence of <tx:annotation-driven /> . 你最初拥有的是所有bean都被ContextLoaderListener加载,并且由于<tx:annotation-driven />的存在而被代理。

Now if you have the same <context:component-scan .../> in your payment-servlet.xml this is going to scan again for all beans creating another instance, however due to the fact that there is no <tx:annotation-driven /> it will not be proxied and no transactions applied. 现在,如果您的payment-servlet.xml中有相同的<context:component-scan .../> ,那么将再次扫描所有创建另一个实例的bean,但是由于没有<tx:annotation-driven />它不会被代理,也不会应用任何交易。

What now happens is that as soon as you need one of your @Service annotated beans the DispatcherServlet looks first in its own ApplicationContext to see if there is bean to satisfy its needs. 现在发生的是,只要您需要一个@Service注释bean, DispatcherServlet首先会在自己的ApplicationContext中查看是否有bean来满足其需求。 If there is it is going to be used (your current case) if there isn't it will consult the parent context (the one loaded by the ContextLoaderListener ). 如果它将被使用(您当前的情况),如果没有它将查询父上下文(由ContextLoaderListener加载的那个)。

What you need to do is configure the ContextLoaderListener to scan for everything BUT @Controller annotated beans and the DispatcherServlet to scan ONLY for @Controller annotated beans. 您需要做的是配置ContextLoaderListener以扫描所有内容但是 @Controller注释bean和DispatcherServlet 扫描@Controller注释bean。 This can be done by configuring the <context:component-scan .../> correctly. 这可以通过正确配置<context:component-scan .../>

applicationContext.xml applicationContext.xml中

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

payment-servlet.xml 支付-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>

This will give you transactions and only single instances of your beans. 这将为您提供事务,并且只为您的bean提供单个实例。 You should remove the <tx:annotation-driven /> from the payment-servlet.xml file. 您应该从payment-servlet.xml文件中删除<tx:annotation-driven />

There is still an open JIRA issue to have this included in the reference guide. 仍然存在一个公开的JIRA问题 ,将其包含在参考指南中。 Also a thread in the Spring Community Forums explaining this. 也是Spring社区论坛中的一个主题解释了这一点。

Looking at your applicationContext.xml again, I noticed you're not assigning an entityManager with your transactionManager declaration. 再次查看您的applicationContext.xml,我注意到您没有使用您的transactionManager声明分配entityManager。 I'm not sure if Spring will implicitly set it, but if it doesn't it would explain why your persists are not working. 我不确定Spring是否会隐式设置它,但如果它不能解释为什么你的坚持不起作用。

For example, change: 例如,更改:

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

UPDATE UPDATE

I've only configured Spring+JPA with Container-managed Entity (looks like your intention as well) - never really Application-managed. 我只使用容器管理的实体配置了Spring + JPA(看起来也是你的意图) - 从来没有真正的应用程序管理。 From what I know, in Container-managed with Spring, you don't really need to configure a Persistent Unit. 据我所知,在使用Spring进行Container管理时,您并不需要配置持久单元。 I'm not 100% sure if this will help but try changing how your entityManagerFactory is declared in applicationContext.xml. 我不是100%确定这是否有帮助,但尝试更改在applicationContext.xml中声明entityManagerFactory的方式。

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

When using annotation-driven transactions, you should put @Transactional annotation before your methods which are working with database. 使用注释驱动的事务时,应该在使用数据库的方法之前放置@Transactional注释。 It could be either your DAO or Service methods. 它可以是您的DAO或服务方法。 I can see that you have put @Transactional on top of PaymentServiceImpl but that is not the right place for that annotation. 我可以看到你已经将@Transactional置于PaymentServiceImpl之上,但这不是该注释的正确位置。

It seems you miss the @Transactional annotation at the start of your DAO implementation class. 看来你在DAO实现类的开头就错过了@Transactional注释。 It would explain why you don't have any transaction opened. 它可以解释为什么你没有打开任何交易。

If it is not enough to solve your problem, could you give us your complete DAO implementation class ? 如果它不足以解决您的问题,您能否给我们完整的DAO实施课程?

managed to find the problem. 设法找到问题。 Spring Docu "@EnableTransactionManagement and only looks for @Transactional on beans in the same application context they are defined in." Spring Docu “@EnableTransactionManagement只在bean中查找它们所定义的相同应用程序上下文中的@Transactional。” Meaing in my payment-servlet.xml i had no <tx:annotation-driven /> thats why there was no active transaction. 在我的payment-servlet.xml中我没有<tx:annotation-driven />这就是没有活动事务的原因。

after creating transactionManager bean you should enable the transaction in Xml file, 在创建transactionManager bean之后,你应该在Xml文件中启用事务,

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

or 要么

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

if u written 2nd one then you have to use @Transaction() in your DAO class, 如果你写了第二个,那么你必须在你的DAO类中使用@Transaction(),

if any method required the transaction then before that write 如果任何方法在写入之前需要事务

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

it tell that transaction is require and readOnly=false means you can read and write the data. 它告诉事务是require,readOnly = false意味着你可以读写数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM