views:

40

answers:

1

Using spring MVC, how would I create a form that doesn't map to a entity (i.e. it has properties from multiple entities).

I want to also validate and use their 'results' object that has the errors collection etc.

Any online examples of this? I need to see it to understand it (newbie)

A: 

You'd simply create a new class containing all the properties you need for the form and use this as model attribute for your form. On the receiving call you can then use this type, too. Spring will automatically bind properties for you. You should also consider using JSR-303 validation annotations.

The general approach is to load all the necessary entities to create the form backing object from on the GET request. Then you put that form backing object in the model. On the POST/PUT request you have to reconstruct the actual entities touched. Typically you load them again then and apply the new submitted partial data to them.

In general it might be a good idea to construct a dedicated component to handle that assembling behaviour for you to not pollute the controller with that code.

/**
 * Prepares the form by setting up a custom form backing object.
 */
@RequestMapping(value = "account/{id}", method = GET)
public String processGet(@PathVariable("id") Long id, Model model) {

  Account account = accountService.getAccount(id);
  return prepareForm(dtoAssembler.createFormFor(account), model);
}


/**
 * Process form submission. Uses JSR-303 validation and explicit error 
 * handling.
 */  
@RequestMapping(value = "account/{id}", method = PUT)
public String processGet(@ModelAttribute("accountForm") @Valid AccountForm form, Errors errors, Model model) {

  if (errors.hasErrors()) {
    return prepareForm(form, model);
  }

  Account account = accountService.getAccount(form.getId());
  accountService.save(dtoAssembler.applyData(account, form));

  return "redirect:/accounts";
}


/**
 * Populates the given {@code Model} with the given {@code AccountForm}
 * and returns the view to show the form.
 */     
private String prepareForm(AccountForm form, Model model) {
  model.addAttribute("accountForm", form);
  return "account";
}

I just wrote it this way here to emphasize what's going on. In a real world scenario I'd probably let the DtoAssembler do all the work with the service (so I'd inject the service into the assembler).

To ease transferring data from the DTO into the domain object using Dozer, BeanUtils or something similar is probably reasonable.

Oliver Gierke
so on the get request, I will load the entities, then load the class containing the form properties. On the post, I will do what? (little pseudo code maybe?) thanks!
Blankman
Updated my post regarding your comment :)
Oliver Gierke