项目是Java Web;
构建工具:maven,
使用一个商品管理系统,用于管理超市和商品。
项目结构如下:
执行如下操作时报错:
点击“delete”,删除超市时,报错。
注意:是有时候报错,不是每次删除都报错。
报错信息如下:
- Struts Problem Report
- Struts has detected an unhandled exception:
- Messages:
- Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
- Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
- File: org/hibernate/jdbc/Expectations.java
- Line number: 81
- Stacktraces
- org.springframework.orm.hibernate4.HibernateOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
- org.springframework.orm.hibernate4.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:181)
- org.springframework.orm.hibernate4.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:680)
- org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:562)
- org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:755)
- org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724)
- org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:475)
- org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:270)
- org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
- org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
- org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91)
- org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
- org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
- com.shop.jn.dao.SupermarketDao$$EnhancerByCGLIB$$2c3435ff.delete()
- com.shop.jn.action.supermarket.DeleteSupermarketAction.execute(DeleteSupermarketAction.java:20)
- sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
- sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
- java.lang.reflect.Method.invoke(Method.java:597)
- com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:453)
- com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:292)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:255)
- com.shop.jn.interceptor.LoggerInterceptor.intercept(LoggerInterceptor.java:23)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:256)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:176)
- com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:265)
- org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:68)
- com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:138)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:236)
- com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:236)
- com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:190)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:75)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:90)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:243)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:100)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:141)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:145)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171)
- com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:176)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:192)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:187)
- com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
- org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54)
- org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:511)
- org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
- org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91)
- org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
- org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
- org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
- org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
- org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
- org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
- org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
- org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
- org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:859)
- org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:579)
- org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1555)
- java.lang.Thread.run(Thread.java:662)
- org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
原因分析:
执行删除超市的action 是DeleteSupermarketAction,内容如下:
- package com.shop.jn.action.supermarket;
- import com.opensymphony.xwork2.ActionSupport;
- import com.shop.jn.dao.SupermarketDao;
- import com.shop.jn.entity.Supermarket;
- /***
- * 删除超市.
- *
- * @author huangwei
- *
- */
- public class DeleteSupermarketAction extends ActionSupport {
- private static final long serialVersionUID = 7753616847784594121L;
- private Supermarket supermarket;
- private SupermarketDao supermarketDao;
- @Override
- public String execute() throws Exception {
- this.supermarketDao.delete(this.supermarket);
- return super.execute();
- }
- public Supermarket getSupermarket() {
- return supermarket;
- }
- public void setSupermarket(Supermarket supermarket) {
- this.supermarket = supermarket;
- }
- public SupermarketDao getSupermarketDao() {
- return supermarketDao;
- }
- public void setSupermarketDao(SupermarketDao supermarketDao) {
- this.supermarketDao = supermarketDao;
- }
- }
说明:supermarket 是通过依赖注入的,注入的字段是id(http://localhost:8080/shop_goods/supermarket/deleteSupermarket.action?supermarket.id=9)。
此时supermarket 的状态是detached 脱管的(脱离管理的),this.supermarketDao.delete 的方法体如下:
- this.sessionFactory.getCurrentSession().delete(obj);
但是hibernate api中对delete 的方法说明如下:
void |
|
要求参数必须是persistent 持久化的的状态。
如何解决呢?
使用原生的sql语句
我在dao中新增了一个方法:
- public void deleteHQL(int id) {
- this.sessionFactory.getCurrentSession()
- .createQuery("delete from Supermarket where id=?")
- .setInteger(0, id).executeUpdate();
- }
在DeleteSupermarketAction 中调用如下:
- @Override
- public String execute() throws Exception {
- this.supermarketDao.deleteHQL(this.supermarket.getId());
- return super.execute();
- }