Monday, July 30, 2018

Spring - Construct new modelAndView from existing BindingResult and Model

Leave a Comment

I am trying to improve my Spring MVC application to use a global exception handler to catch various persistence exceptions across all my controllers. This is the controller code that runs when the user tries to save a new StrengthUnit object for example. All the validations work perfectly fine and the form is correctly returned with an error message underneath the name field when the PersistenceException is thrown. The resulting page also correctly contains the strengthUnit attribute and is able to bind the field (this entity just has a name field) back to the form :

@RequestMapping(value = {"/newStrengthUnit"}, method = RequestMethod.POST) public String saveStrengthUnit(@Valid StrengthUnit strengthUnit, BindingResult result, ModelMap model) throws Exception {     try     {         setPermissions(model);          if (result.hasErrors())         {             return "strengthUnitDataAccess";         }         strengthUnitService.save(strengthUnit);          session.setAttribute("successMessage", "Successfully added strength unit \"" + strengthUnit.getName() + "\"!");     }     catch (PersistenceException ex)     {         FieldError error = new FieldError("strengthUnit", "name", strengthUnit.getName(), false, null, null,                  "Strength unit \"" + strengthUnit.getName() + "\" already exists!");         result.addError(error);          return "strengthUnitDataAccess";     }      return "redirect:/strengthUnits/list"; } 

I am trying to use this as a starting point to incorporate the global exception handler that i built and I don't understand how that handler can be called and return the same page with the same model and binding result. I have tried something extremely ugly with a custom exception just to try and understand the mechanics and get the handler to return me the same page as before and I'm unable to get it to work.

Here is the custom exception I built :

public class EntityAlreadyPersistedException extends Exception {     private final Object entity;     private final FieldError error;     private final String returnView;     private final ModelMap model;     private final BindingResult result;      public EntityAlreadyPersistedException(String message, Object entity, FieldError error, String returnView, ModelMap model, BindingResult result)      {         super(message);          this.entity = entity;         this.error = error;         this.returnView = returnView;         this.model = model;         this.result = result;     }      public Object getEntity()      {         return entity;     }      public FieldError getError()      {         return error;     }      public String getReturnView()      {         return returnView;     }      public ModelMap getModel()     {         return model;     }      public BindingResult getResult()      {         return result;     } } 

Here is my modified catch block in my controller's saveStrengthUnit method :

catch (PersistenceException ex)     {         FieldError error = new FieldError("strengthUnit", "name", strengthUnit.getName(), false, null, null,                  "Strength unit \"" + strengthUnit.getName() + "\" already exists!");         result.addError(error);          throw new EntityAlreadyPersistedException("Strength unit \"" + strengthUnit.getName() + "\" already exists!", strengthUnit, error,                  "strengthUnitDataAccess", model, result);     } 

And finally the global exception handler's method to catch it :

@ExceptionHandler(EntityAlreadyPersistedException.class) public ModelAndView handleDataIntegrityViolationException(HttpServletRequest request, Exception ex) {     EntityAlreadyPersistedException actualException;      actualException = ((EntityAlreadyPersistedException)ex);      ModelAndView modelAndView = new ModelAndView();     modelAndView.setViewName(actualException.getReturnView());     modelAndView.addObject(BindingResult.MODEL_KEY_PREFIX + "strengthUnitForm", actualException.getResult());      if (actualException.getEntity() instanceof StrengthUnit)     {         modelAndView.addObject("strengthUnit", (StrengthUnit)actualException.getEntity());     }     return modelAndView; } 

This is extremely ugly and probably very foolish to an experienced Spring developer but I am not quite one (yet). This works BUT the binding result is lost and the validation errors do not appear. How can I modify this code to behave as it was before while still using the global exception handler to handle all errors?

Thanks!

2 Answers

Answers 1

If you are trying to catch the valid exception , which was throw when you are using the @Valid .And you want same handler handle that exception ,then add one more exception class in the @ExceptionHandler annotation

What the doc says

The @ExceptionHandler value can be set to an array of Exception types. If an exception is thrown matches one of the types in the list, then the method annotated with the matching @ExceptionHandler will be invoked. If the annotation value is not set then the exception types listed as method arguments are used.

The exception thrown by the @Valid annotation is MethodArgumentNotValidException , So you can add this exception on same handler method

I hope this may help you

Answers 2

1.you are setting the entity as strengthUnit.getName() not strengthUnit

    throw new EntityAlreadyPersistedException("Strength unit \"" + strengthUnit.getName() + "\" already exists!", strengthUnit, error,                      "strengthUnitDataAccess", model, result); 

2.but you are checking if (actualException.getEntity() instanceof StrengthUnit

Hope it helps, Try to set the entity as strengthUnit.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment