【发布时间】:2013-11-19 18:07:01
【问题描述】:
我开始使用 springapp 示例学习 Spring 进行 Web 开发。现在,我正在尝试提高列表中每个产品的价格(从数据库中检索),然后将新价格保存回数据库。它可以检索产品列表,但合并不做任何事情。我将 .flush() 添加到代码中,它给出了一个丑陋的异常(就在应用程序启动时)。
这是应用程序启动时抛出的异常(在调用方法调用 .flush() 之前):
javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:993)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:366)
at $Proxy18.flush(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:241)
at $Proxy18.flush(Unknown Source)
at org.springtest.mavenspringapp.repository.JPAProductDao.saveProduct(JPAProductDao.java:34)
at org.springtest.mavenspringapp.service.SimpleProductManager.increasePrice(SimpleProductManager.java:35)
at org.springtest.mavenspringapp.web.PriceIncreaseFormController.onSubmit(PriceIncreaseFormController.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:920)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:827)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:801)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
这是一次我提高每个产品的价格并调用 .merge() 后跟 .flush():
javax.persistence.TransactionRequiredException: no transaction is in progress
org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:993)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:366)
$Proxy18.flush(Unknown Source)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:241)
$Proxy18.flush(Unknown Source)
org.springtest.mavenspringapp.repository.JPAProductDao.saveProduct(JPAProductDao.java:34)
org.springtest.mavenspringapp.service.SimpleProductManager.increasePrice(SimpleProductManager.java:35)
org.springtest.mavenspringapp.web.PriceIncreaseFormController.onSubmit(PriceIncreaseFormController.java:38)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:920)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:827)
javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:801)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
这是 ProductDao:
@Repository(value = "productDao")
public class JPAProductDao implements ProductDao {
private EntityManager em = null;
/*
* Sets the entity manager.
*/
@PersistenceContext
public void setEntityManager(EntityManager em) {
this.em = em;
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public List<Product> getProductList() {
return em.createQuery("select p from Product p").getResultList();
}
@Transactional(readOnly = false)
public void saveProduct(Product prod) {
em.merge(prod);
em.flush();
}
}
这是我的控制器中处理涨价表单的方法:
@RequestMapping(method = RequestMethod.POST)
public String onSubmit(@Valid PriceIncrease priceIncrease, BindingResult result)
{
if (result.hasErrors())
{
return "priceincrease";
}
int increase = priceIncrease.getPercentage();
logger.info("Increasing prices by " + increase + "%.");
productManager.increasePrice(increase);
return "redirect:/hello.htm";
}
ProductManager 是由一个名为 SimpleProductManager 的类实现的服务。此类更新每个产品的价格并尝试通过 JpaProductDao 将其保存到数据库中。
这是我的 applicationContext.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:p="http://www.springframework.org/schema/p"
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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- holding properties for database connectivity /-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- enabling annotation driven configuration /-->
<context:annotation-config/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:jpaVendorAdapter-ref="jpaAdapter">
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
<property name="persistenceUnitName" value="springappPU"></property>
</bean>
<bean id="jpaAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:database="${jpa.database}"
p:showSql="${jpa.showSql}"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory"/>
<!-- <tx:annotation-driven transaction-manager="transactionManager"/>-->
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
<context:component-scan base-package="org.springtest.mavenspringapp">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- Scans the classpath of this application for @Components to deploy as beans
<context:component-scan base-package="org.springtest.mavenspringapp.repository" />
<context:component-scan base-package="org.springtest.mavenspringapp.service" />
<context:component-scan base-package="org.springtest.mavenspringapp.web" />-->
</beans>
顺便说一句,我添加了 .flush 方法调用,因为有人在这里建议它(这是关于不持久更改的另一个问题)。
差点忘了,如果不包含 .flush() ,产品不会更新,也不会抛出异常。这是休眠日志输出:
Hibernate: select product0_.id as id1_0_0_, product0_.description as descript2_0_0_, product0_.price as price3_0_0_ from products product0_ where product0_.id=?
Hibernate: select product0_.id as id1_0_0_, product0_.description as descript2_0_0_, product0_.price as price3_0_0_ from products product0_ where product0_.id=?
Hibernate: select product0_.id as id1_0_, product0_.description as descript2_0_, product0_.price as price3_0_ from products product0_
我希望你能帮助我。提前致谢。
编辑:我正在发布我的 web.xml 文件内容:
<?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"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<display-name>Mavenspringapp</display-name>
<servlet>
<servlet-name>mavenspringapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/app-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mavenspringapp</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
</web-app>
app-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-3.2.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-3.2.xsd">
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>
<!-- Scans the classpath of this application for @Components to deploy as beans -->
<context:component-scan base-package="org.springtest.mavenspringapp" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- Configures the @Controller programming model -->
<mvc:annotation-driven/>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView">
</property>
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
【问题讨论】:
-
请发布您的 web.xml。
-
@M.Deinum 我编辑了我的问题,现在包含了我的 web.xml 文件内容
-
能否也添加app-config.xml。
-
还有你的问题......正如我在最初的回答中已经暗示的那样。一旦被代理(
ContextLoaderListener和一次未被代理的DispatcherServlet基本上你正在渲染你所有的事务设置,aop 东西无用),你将加载你的 bean 两次。您的ContextLoaderListener应该扫描所有但是@Controller并且您的DispatcherServlet应该仅 扫描@Controller。 (见我修改后的答案)。 -
从您的
<tx:annotation-driven />中删除mode="aspectj"。因为这需要 AspectJ 加载或编译时编织,而这不是您正在使用的。您也可以删除<context:annotation-driven />,因为<context:component-scan />已经暗示了这一点。@Transactional注释实际上应该在您的SimpleProductManager上,因为那是您的服务层,因此是事务边界。存储库自动参与此事务。
标签: java spring hibernate spring-mvc jpa