Только один метод для добавления / редактирования на POST - PullRequest
0 голосов
/ 08 февраля 2012

В настоящее время у меня есть следующее сопоставление в моем контроллере:

  @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 # в сеансе ...

EDIT Здесь я дал ему попытку ...

Метод контроллера:

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

2 голосов
/ 08 февраля 2012

Похоже, вы пытаетесь сохранить объект с тем же идентификатором, и этот объект не присоединен к сеансу гибернации.

Вы, вероятно, отредактировали имя Person и попытались его сохранить. Человек с таким именем не найден, но человек с таким же идентификатором существует в базе данных, следовательно, ошибка.

Либо используйте merge(), либо, в вашем сервисе, сначала получите человека (чтобы подключить его к сеансу гибернации), обновите необходимые поля и сохраните его в БД.

Чтобы объяснить лучше:

@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.save или service.update). Это действительно зависит от того, насколько сильно вы хотите зависеть от умения спящего.

1 голос
/ 26 мая 2012

Для этого эффективно используйте шаблоны URI в Spring 3.0.

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

}
...