只有一种方法可以在POST上添加/编辑

时间:2012-02-08 15:28:44

标签: hibernate spring spring-mvc spring-3

目前我的控制器中有以下映射:

  @RequestMapping( value = "/add.html", method = RequestMethod.GET )
  public String showAddForm( Map<String, Object> map )
  {
    map.put( "person", new Person() );
    return "form";
  }

  @RequestMapping( value = "/add.html", method = RequestMethod.POST )
  public String processAddForm( @ModelAttribute( "person" ) @Valid Person person, BindingResult result, Map<String, Object> map )
  {
    if ( service.findByName( person.getName() ) != null )
    {
      result.addError( new FieldError( "person", "name", "Person already exists" ) );
    }

    if ( result.hasErrors() )
    {
      return "form";
    }

    service.save( person );
    return "redirect:/index.html";
  }

  @RequestMapping( value = "/{id}/edit.html", method = RequestMethod.GET )
  public String showEditForm( @PathVariable( "id" ) int id, Map<String, Object> map )
  {
    map.put( "person", service.load( id ) );
    return "form";
  }

  @RequestMapping( value = "/{id}/edit.html", method = RequestMethod.POST )
  public String processEditForm( @PathVariable( "id" ) int id, @ModelAttribute( "person" ) @Valid Person person, BindingResult result )
  {
    Person p;
    if ( ( p = service.findByName( person.getName() ) ) != null && person.getId() != id )
    {
      result.addError( new FieldError( "person", "name", "Person already exists" ) );
    }

    if ( result.hasErrors() )
    {
      return "form";
    }

    person.setId( id );
    service.save( person );
    return "redirect:/index.html";
  }

如何减少冗余代码? 也许通过将表单操作设置为/save.html并将processAddForm和processEditForm组合到

@RequestMapping( value = "/save.html", method = RequestMethod.POST )

但是代码应该如何呢? 我只是尝试了它并得到了一些例外,因为会话中已经存在Person#的实例...

修改 在这里,我尝试了一下......

控制器方法:

  @RequestMapping( value = "/save.html", method = RequestMethod.POST )
  public String onSubmit( @ModelAttribute( "person" ) @Valid Person person, BindingResult result, Map<String, Object> map )
  {
    Person p;
    if ( ( p = service.findByName( person.getName() ) ) != null )
    {
      if ( person.getId() != p.getId() )
      {
        result.addError( new FieldError( "person", "name", "Person already exists" ) );
      }
    }

    if ( result.hasErrors() )
    {
      return "form";
    }

    service.save( person );
    return "redirect:/index.html";
  }

形式:

<c:url value="/save.html" var="formAction" />
<form:form modelAttribute="person" action="${formAction}" method="post">
  <form:errors path="*" cssClass="error" element="div" />
  <form:hidden path="id" />
  <form:label path="name">Name:</form:label>
  <form:input path="name" />
  <input type="submit" value="Submit" />
</form:form>

添加作品,但编辑提供以下例外:

org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.example.entities.Person#7]
    org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:637)
    org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:305)
    org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:246)
    org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:112)
    org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
    org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:677)
    org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:669)
    org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:665)
    com.example.dao.PersonDaoImpl.save(PersonDaoImpl.java:30)
    com.example.dao.PersonDaoImpl.save(PersonDaoImpl.java:1)
    com.example.services.PersonServiceImpl.save(PersonServiceImpl.java:29)
    com.example.services.PersonServiceImpl.save(PersonServiceImpl.java:1)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:616)
    org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
    org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    $Proxy44.save(Unknown Source)
    com.example.controller.PersonController.onSubmit(PersonController.java:65)
    com.example.controller.PersonController$$FastClassByCGLIB$$9c2dc698.invoke(<generated>)
    net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:61)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
    com.example.controller.PersonController$$EnhancerByCGLIB$$a0aff0d2.onSubmit(<generated>)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:616)
    org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:212)
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:900)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:311)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:116)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
    org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
    org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:101)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
    org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
    org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
    org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
    org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:201)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:173)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
    org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)

EDIT2: PersonServiceImpl:

  @Override
  @Transactional
  public void save( Person person )
  {
    dao.save( person );
  }

PersonDaoImpl:

  @Override
  public void save( Person person )
  {
    try
    {
      sessionFactory.getCurrentSession().saveOrUpdate( person );
    }
    catch( NonUniqueObjectException e )
    {
      sessionFactory.getCurrentSession().merge( person );
    }
  }

这是正确的方法吗?也许是一个更好的解决方案?

EDIT3

  @Override
  public void save( Person person )
  {
    if ( load( person.getId() ) != null)
    {
      sessionFactory.getCurrentSession().merge( person );
    }
    else
    {
      sessionFactory.getCurrentSession().save( person );
    }
  }

  @Override
  public Person load( int id )
  {
    return ( Person ) sessionFactory.getCurrentSession().load( Person.class, id );
  }

这是一个更好的解决方案吗?

2 个答案:

答案 0 :(得分:2)

看起来您正在尝试保存具有相同ID的对象,并且该对象未附加到hibernate会话。

您可能已编辑Person的名称并尝试保存。找不到具有该名称的人,但数据库中存在具有相同ID的人,因此出错。

使用merge()或者在您的服务中首先获取此人(使其连接到hibernate的会话),更新必要的字段并将其保存到DB。

更好地解释:

@RequestMapping( value = "/save.html", method = RequestMethod.POST )
  public String onSubmit( @ModelAttribute( "person" ) @Valid Person person, BindingResult result, Map<String, Object> map )
  {
    Person p;
  // You changed the name so it doesn't find anything here and proceeds without errors
    if ( ( p = service.findByName( person.getName() ) ) != null ) 
    {
      if ( person.getId() != p.getId() )
      {
        result.addError( new FieldError( "person", "name", "Person already exists" ) );
      }
    }

    if ( result.hasErrors() )
    {
      return "form";
    }

//the code breaks here, person is not attached to hibernate session and you're trying to save it. Modify your service code to use merge() or to fetch the person from database before saving it.
    service.save( person );  
    return "redirect:/index.html";
  }

修改

如果您使用merge(),则无需进行任何测试。 Hibernate非常聪明,可以知道您是保存新实例还是将更改保存到已经存在的实例。另一种方法是在控制器中测试是否设置了person.id,并在您的服务中调用适当的方法(service.saveservice.update)。这真的取决于你希望依赖于hibernate的聪明程度。

答案 1 :(得分:1)

有效地使用Spring 3.0中的URI模板。

@RequestMapping( value = "/save.html{mode}", method = RequestMethod.POST ) 
public EditAndSave(@PAthVariable String mode,...){
 if(mode.equals("Edit")){
   //write code for edit here
 }

 if(mode.equals("Add")){
   //write code for add here
 }

}