0

I have this Object

public class Deportista implements Serializable {

    private static final long serialVersionUID = 6229604242306465153L;

    private String id;
    ...

    @NotNull(message="{field.null}")
    public String getId() {
        return id;
    }
    ...

}

I have the following Controller's methods

@InitBinder(value="deportistaRegistrar")
public void registrarInitBinder(WebDataBinder binder) {
    logger.info(">>>>>>>> registrarInitBinder >>>>>>>>>>>>>");
}

@RequestMapping(value="/registrar.htm", method=RequestMethod.GET)
public String crearRegistrarFormulario(Model model){
    logger.info("crearRegistrarFormulario GET");
    Deportista deportista = new Deportista();
    model.addAttribute("deportistaRegistrar", deportista);
    return "deportista.formulario.registro";
}

@RequestMapping(value="/registrar.htm", method=RequestMethod.POST)
public String registrarPerson(@Validated @ModelAttribute("deportistaRegistrar") Deportista deportista, 
                              BindingResult result){

    logger.info("registrarPerson POST");
    logger.info("{}", deportista.toString());

    if(result.hasErrors()){
        logger.error("There are errors!!!!");

        for(ObjectError objectError : result.getAllErrors()){
            logger.error("Error {}", objectError);
        }

        return "deportista.formulario.registro";
    }

    logger.info("All fine!!!!");
    this.fakeMultipleRepository.insertDeportista(deportista);
    return "redirect:/manolo.htm";
}

Until here the Controller is able to create a form (GET) and submit (POST) a new command object, Validation code works well.

The problem is with the update.

I have the following:

@InitBinder(value="deportistaActualizar")
public void actualizarInitBinder(WebDataBinder binder) {
    logger.info(">>>>>>>> actualizarInitBinder >>>>>>>>>>>>>");
    binder.setDisallowedFields("id");
}

Observe I have binder.setDisallowedFields("id")

public String crearActualizarFormulario(@PathVariable("id") String id, Model model){
    logger.info("crearActualizarFormulario GET");
    Deportista deportista = this.fakeMultipleRepository.findDeportista(id);
    model.addAttribute("deportistaActualizar", deportista);
    return "deportista.formulario.actualizacion";
}   

@RequestMapping(value="/{id}/actualizar.htm", method=RequestMethod.POST)
public String actualizarPerson(@Validated @ModelAttribute("deportistaActualizar") Deportista deportista, 
                           BindingResult result){

    logger.info("actualizarPerson POST");
    logger.info("{}", deportista.toString());

    if(result.hasErrors()){
        logger.error("There are errors!!!!");

        for(ObjectError objectError : result.getAllErrors()){
            logger.error("Error {}", objectError);
        }

        return "deportista.formulario.actualizacion";
    }

    logger.info("All fine!!!!");
    this.fakeMultipleRepository.updateDeportista(deportista);
    return "redirect:/manolo.htm";
}

The problem is:

  • when the form or command has any error, the controller re-render the view and the form appear showing the error messages how is expected, but without the ID value

or

  • if I try to update the object, of course keeping the id value, and without any error to simply proceed to update, it fails

The following appears in the Console:

- -------- createCollections ---------------
- >>>>>>>> actualizarInitBinder >>>>>>>>>>>>>
- Skipping URI variable 'id' since the request contains a bind value with the same name.
- actualizarPerson POST
- Deportista [id=null, nombre=Manuel, ...]
- There are errors!!!!
- Error Field error in object 'deportistaActualizar' on field 'id': rejected value [null]; codes [NotNull.deportistaActualizar.id,NotNull.id,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [deportistaActualizar.id,id]; arguments []; default message [id]]; default message [The field must be not empty]

The id is null. How I can around this problem keeping the Request Scope?

I have an alternate controller which is working with @SessionAttributes and all works perfect. But since is a huge risk if the user has many tabs open in the same web browser, one for create and other for updating, all is going to be very wrong. According with Spring MVC + Session attributes and multiple tabs, request scope should be used instead of session scope. It has sense.

Sadly seems Spring is not going to fix this: @SessionAttributes doesn't work with tabbed browsing

Addition

According with your suggestion, I have the following:

@ModelAttribute("deportistaActualizar")
public Deportista populateActualizarFormulario(@RequestParam(defaultValue="") String id){
    logger.info("populateActualizarFormulario - id: {}", id);
    if(id.equals(""))
        return null;
    else
        return this.fakeMultipleRepository.findDeportista(id);
}

Observe the method uses @RequestParam, my problem is how update that method to work when the URL to update has the following style http://localhost:8080/spring-utility/deportista/1/actualizar.htm. There is no param in the URL, therefore @RequestParam is useless now.

I already have read the Spring Reference documentation: Using @ModelAttribute on a method

Second Addition

Yes, you was right, and I did that yesterday, but I forget to share the following:

@ModelAttribute("deportistaActualizar")
public Deportista populateActualizarFormulario(@PathVariable(value="id") String id){
    logger.info("populateActualizarFormulario - id: {}", id);
    if(id.equals(""))
        return null;
    else
        return this.fakeMultipleRepository.findDeportista(id);
}

Since a @ModelAttribute is called always before by any handler method, the following URL fails http://localhost:8080/spring-utility/deportista/registrar.htm, the following appears on the page

HTTP Status 400 -
type Status report
message
description The request sent by the client was syntactically incorrect.

Of course because the URL does not contains the expected id. Therefore I can't create new records to later edit/see.

I can confirm, that for the following work:

  • http://localhost:8080/spring-utility/deportista/1/detalle.htm
  • http://localhost:8080/spring-utility/deportista/1/actualizar.htm

the id (1) is retrieved.

How I could resolve this?

Thank You

Community
  • 1
  • 1
Manuel Jordan
  • 15,253
  • 21
  • 95
  • 158
  • The problem is that you are using 2 methods which basically operate on the same object. However they don't share the object. As I also answered in the other question add a method annotated with `@ModelAttribtue` which retrieves the desired object based on the id. Basically remove the retrieval of the object from the get method and put it in a method annotated with `@ModelAttribute`. – M. Deinum Sep 18 '14 at 19:20
  • Understood, but see the `Addition` section. Thank You. – Manuel Jordan Sep 18 '14 at 20:23
  • Just place a `@PathVariable` on there almost all the annotation usable on a `@RequestMapping` method apply to `@ModelAttribute` methods. – M. Deinum Sep 19 '14 at 05:03
  • Could you check the `Second Addition` section please? Thank You. – Manuel Jordan Sep 19 '14 at 12:39
  • Currently there is no way to define a default or optional path variable. As a workaround you can create 2 controllers one for creating a new one, the other for updating existing ones. – M. Deinum Sep 19 '14 at 13:30
  • That was I thought, the *worst* scenario. Just wondered how you around the problem about `@SessionAttributes` with the problem of "multiple tabs". Thank You. – Manuel Jordan Sep 19 '14 at 15:12
  • I am going to create a JIRA issue (improvement) with Spring 4 & 4.1 out there, it should be fix it. Thanks by all Marten! – Manuel Jordan Sep 19 '14 at 15:20
  • I have created two controllers, it works now. Thank You – Manuel Jordan Sep 19 '14 at 23:03

0 Answers0