Spring - best way to deal with binding to a list of beans in a simpleformcontroller - spring

Without thinking about it too much, I've been doing something like the following:
1) building a list of SomeBean objects based on the results of a database call
2) putting that list in my command object
3) building a form based on that command object where users can modify attributes of the SomeBeans
4) extracting data out of the post-submit command object and writing the updated data to my database
My code looks something like this:
public class UpdateThingsinListController extends SimpleFormController {
protected Object formBackingObject(final HttpServletRequest request)
throws Exception {
List<SomeBean> beans = database.getBeans();
Command comamnd = new UpdateThingsCommand()
command.setList(beans);
return command;
}
protected ModelAndView onSubmit(final HttpServletRequest request,
final HttpServletResponse response, final Object commandArg,
final BindException errors) throws Exception {
database.setBeans(commandArg.getList());
}
}
my jsp looks somthething like:
<form:form>
<c:forEach var="bean" items="${beans}" varStatus="status">
<form:checkbox path="beans[${status.index}].someBooleanProperty" />${bean.name} <br>
</c:forEach>
</form:form>
The code works fine, but it's just dawned on me that my "beans" list is getting created twice (sessionform must be false in my case) -- once when displaying the form, once when binding. If anything changes on the second creation (a bean is missing, the results are in a different order), my binding will get messed up, and I'll get fired. I'm beginning to think that any biding scheme where a command object needs to be merged with a form submission is very risky.
So, my question is -- how do folks ensure that form submissions get bound to lists correctly? Is there another way to do it besides list index? Object ids maybe?
thanks,
-Morgan

If the sessionForm is set to false then during the submit the command is recreated in formBackingObject method. Order of the list in the onSubmit method will be exactly like order of the binded objects on page.

If you can't depend on the List always being the same whenever you retrieve it, then you have to use a different collection - a map.
Another approach would be to cache your List somewhere. Perhaps some AOP around your database.getBeans method.

The quick way is to first delete all existing SomeBeans from the database and create the ones bound on submit.
A more elegant way might be to put the unique identifier of the SomeBean in a hidden form field.

Related

#ModelAttribute returns a new instance on form submit

I am working on a Spring MVC based application. The process flow is as follows:
Fetch the data from DB (table mapped to a POJO)
Display a form backed by the POJO (from step 1). Not all the fields are displayed (like Primary Key etc).
User can update some field value in the form and will then submit.
On receving the updated POJO using #ModelAttribute annotation in my Controller, I found that not all the fields are populated in the POJO received via #ModelAttribute. All the fields which were not mapped on the JSP (like primary key) are set to null or their default value in case of primitives. Due to this I am not able to update the same in the DB.
One solution that I found is I can use fields but that does not sound much efficient solution as I have a large number of attributes which are not displayed on the JSP page.
A model attribute is simply a glorified request attribute. Request attributes only live for the duration of one request-response cycle.
HTTP request -> Get POJO from DB -> Add POJO to model attributes -> Render JSP -> HTTP response
After that, the request attributes are eventually GC'ed since they are no longer reachable by the application (the servlet container makes sure of that).
The next request you send will have its set of new request attributes with no relation to the previous requests'.
When you generate a <form> from a model attribute, Spring creates the <input> elements from the fields of the model attribute which you choose. When you eventually submit the form, only those values will be sent as request parameters in the request. Your application will therefore only have access to those to generate the new model attribute.
You seem to need Session attributes or Flash attributes (which are really just short-lived session attributes).
Please if somebody know a better solution please let me know, but on my application we are always sending back all those id´s and others values that we want to persist in the request response with hidden fields, but I think is a little bit risk, for example in case of id´s. which could be used for SQLInjections attacks.
You could use path variable to transport the primary key (kind of rest url ...) and make use of all the magic of Spring :
create a DefaultFormattingConversionService bean (to keep default conversions)
register (in that ConversionService) a Converter converting a String in your POJO (string -> primary key -> object from database)
directly use the path variable in your controller methods
#RequestMapping(value=".../{data}/edit", method=RequestMethod.GET)
public String edit(#ModelAttribute("data") Pojo pojo) {
return "view_with_form";
}
#RequestMapping(value=".../{data}/edit", method=RequestMethod.POST)
public String update(#ModelAttribute("data") Pojo pojo, BindingResult result) {
if (result.hasErrors()) {
return "view_with_form";
}
return "redirect:.../data/edit";
}
When you give a ModelAttribute to a controller method, Spring tries to find it in the session or in a path variable. And even if you didn't asked for it, the error management is not very expensive ...

ArgumentResolvers within single transaction?

I am wondering if there is a way to wrap all argument resolvers like for #PathVariables or #ModelAttributes into one single transaction? We are already using the OEMIV filter but spring/hibernate is spawning too many transactions (one per select if they are not wrapped within a service class which is be the case in pathvariable resolvers for example).
While the system is still pretty fast I think this is not necessary and neither consistent with the rest of the architecture.
Let me explain:
Let's assume that I have a request mapping including two entities and the conversion is based on a StringToEntityConverter
The actual URL would be like this if we support GET: http://localhost/app/link/User_231/Item_324
#RequestMapping("/link/{user}/{item}", method="POST")
public String linkUserAndItem(#PathVariable("user") User user, #PathVariable("item") Item item) {
userService.addItem(user, item);
return "linked";
}
#Converter
// simplified
public Object convert(String classAndId) {
return entityManager.find(getClass(classAndId), getId(classAndId));
}
The UserService.addItem() method is transactional so there is no issue here.
BUT:
The entity converter is resolving the User and the Item against the database before the call to the Controller, thus creating two selects, each running in it's own transaction. Then we have #ModelAttribute methods which might also issue some selects again and each will spawn a transaction.
And this is what I would like to change. I would like to create ONE readonly Transaction
I was not able to find any way to intercept/listen/etc... by the means of Spring.
First I wanted to override the RequestMappingHandlerAdapter but the resolver calls are well "hidden" inside the invokeHandleMethod method...
The ModelFactory is not a spring bean, so i cannot write an interceptor either.
So currently I only see a way by completely replacing the RequestMappingHandlerAdapter, but I would really like to avoid that.
And ideas?
This seems like a design failure to me. OEMIV is usually a sign that you're doing it wrong™.
Instead, do:
#RequestMapping("/link/User_{userId}/Item_{itemId}", method="POST")
public String linkUserAndItem(#PathVariable("userId") Long userId,
#PathVariable("itemId") Long itemId) {
userService.addItem(userId, itemId);
return "linked";
}
Where your service layer takes care of fetching and manipulating the entities. This logic doesn't belong in the controller.

Storing session data in controller

I'm new to Spring. I'm working on a MVC application that would works as follows:
1) user fills the form with data necessary to create the connection to some service
2) controller gets the data from input, create new object serviceManager and save this object e.g in some HashMap with serviceId
3) next time user wants to use this service, controller using serviceId reads data from HashMap.
So I simply need to store this HashMap throughout the whole session in my controller for future use. What would be the best way to accomplish that? Maybe creating serviceManager object each time and reading data from database is the proper solution? In my controller I'm already using #Autowired fields which perfectly serve the purpose, but they're defined in spring xml and I have to store the data dynamically.
Seems your requirement is kind of same with mine which I should keep the main data in the session and every time get the detail data from client and combine 2 kind of data to retrieve something from database. I just put the main part data in the session and then in the whole session that I can get it. I also try to use #SessionAttribute, but after tried dozens of time, I gave it up, it has a lots of problems. So if you can, I just recomment you to store the data in session, that's the samplest way.
I'm newish to spring myself, but as far as putting this in the session:
#Controller
#SessionAttributes({"myObject"})
public class MyController() {
...
#RequestMapping(value="/foo")
// Corrected as per Costi below
// public String someMethod(#PathVariable MyObject myObject) {
public String someMethod(#ModelAttribute MyObject myObject) {
...
}
}
#SessionAttributes will put a MyObject named myObject into the session, if it's not already there, and the #PathVariable pulls it down so you can use it in the method.
The curlys in session attributes aren't necessary for just one attribute, however, you can specify more than one, comma separated, when you use the array notation (which is to say: the curlys)

Work flow of simpleFormcontroller in spring MVC 3.0

I have seen many examples on how simpleFormcontroller works.
But still I have some confusion.
I want to know when formBackingObject(), referenceData(), onSubmit() methods invoked?
I dont know exact working flow of these methods?
Can anyone explain me?
Workflow is as follows and it is controlled by AbstractFormController class-
The controller receives a request for a new form (typically a GET).
Call to formBackingObject() which by default, returns an instance of the commandClass that has been configured (see the properties the superclass exposes), but can also be overridden to e.g. retrieve an object from the database (that needs to be modified using the form).
Call to initBinder() which allows you to register custom editors for certain fields (often properties of non-primitive or non-String types) of the command class. This will render appropriate Strings for those property values, e.g. locale-specific date strings.
Only if bindOnNewForm is set to true, then ServletRequestDataBinder gets applied to populate the new form object with initial request parameters and the onBindOnNewForm(HttpServletRequest, Object, BindException) callback method is called. Note: any defined Validators are not applied at this point, to allow partial binding. However be aware that any Binder customizations applied via initBinder() (such as DataBinder.setRequiredFields(String[]) will still apply. As such, if using bindOnNewForm=true and initBinder() customizations are used to validate fields instead of using Validators, in the case that only some fields will be populated for the new form, there will potentially be some bind errors for missing fields in the errors object. Any view (JSP, etc.) that displays binder errors needs to be intelligent and for this case take into account whether it is displaying the initial form view or subsequent post results, skipping error display for the former.
Call to showForm() to return a View that should be rendered (typically the view that renders the form). This method has to be implemented in subclasses.
The showForm() implementation will call referenceData(), which you can implement to provide any relevant reference data you might need when editing a form (e.g. a List of Locale objects you're going to let the user select one from).
Model gets exposed and view gets rendered, to let the user fill in the form.
The controller receives a form submission (typically a POST). To use a different way of detecting a form submission, override the isFormSubmission method.
If sessionForm is not set, formBackingObject() is called to retrieve a form object. Otherwise, the controller tries to find the command object which is already bound in the session. If it cannot find the object, it does a call to handleInvalidSubmit which - by default - tries to create a new form object and resubmit the form.
The ServletRequestDataBinder gets applied to populate the form object with current request parameters.
Call to onBind(HttpServletRequest, Object, Errors) which allows you to do custom processing after binding but before validation (e.g. to manually bind request parameters to bean properties, to be seen by the Validator).
If validateOnBinding is set, a registered Validator will be invoked. The Validator will check the form object properties, and register corresponding errors via the given Errors object.
Call to onBindAndValidate() which allows you to do custom processing after binding and validation (e.g. to manually bind request parameters, and to validate them outside a Validator).
Call processFormSubmission() to process the submission, with or without binding errors. This method has to be implemented in subclasses.
Source
For more details and diagrammatic representation to understand the flow you can refer to below link -
SimpleFormController is deprecated since Spring 3.0
In Spring 3.0 use one controller with two methods for the creation process (and a third one for the show page). It typical looks like that:
/**
* Shows a form for car creation.
*/
#RequestMapping(params = "form", method = RequestMethod.GET)
public ModelAndView createForm() {
ModelMap uiModel = new ModelMap();
uiModel.addAttribute("carCreateFormBackingObject", new CarCreateFormBackingObject()); //formBackingObject - often called command object
uiModel.addAttribute("manufactureres", this.manufactureresDao.readAll()); //referenceData
return new ModelAndView("car/create", uiModel);
}
/**
* Creates the car and redirects to its detail page.
*
*/
#RequestMapping(method = RequestMethod.POST)
public ModelAndView create(final #Valid CarCreateFormBackingObject carCreateFormBackingObject,
final BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
ModelMap uiModel = new ModelMap();
uiModel.addAttribute("carCreateFormBackingObject", carCreateFormBackingObject);
uiModel.addAttribute("manufactureres", this.manufactureresDao.readAll()); //referenceData
return new ModelAndView("car/create", uiModel);
}
Car car = this.carService.create(carCreateFormBackingObject.name, ...);
return new ModelAndView(new RedirectView("/cars/" + car.getId(), true)); //redirect to show page
}
still i want to know formBackingObject(),refernceData() method get invoked automatically by whom and when?
Back to your question "still i want to know formBackingObject(),refernceData() method get invoked automatically by whom and when?"
All these methods get invoked by SimpleFormController (and its superclass AbstractFormController), the follow the Template-Method-Pattern. - SimpleFormController defines the process and your concrete subclass "plugsin" in some hooks of this process to gain the business value.
formBackingObject in invoked by AbstractFormController when the controller needs to handle a Submit (POST), or build the Command object for the initial "new" view.
referenceData is always invoked AbstractFormController when it need to build the model for the view.
formBackingObject() method, is used when you want to take some action before rendering page. i.e. like default value in HTML components.
refereceData() method, is used for add reference data in your form, i.e. populating dropdowns
OnSubmit() method, is called whe you submit form.
But, if you are using Spring 3.0
Follow following approach using annotation
#RequestMapping(value = "/index.htm", method = RequestMethod.GET)
public String showLogin() {
return "user/login";
}
This will same as formBackingObject. and in this method use modelMap() and add reference data.
Add methods same way with method = POST which will be same as OnSubmit()
rfe folling link
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/web/servlet/mvc/SimpleFormController.html
Read Workflow.. you can simply understand your doubts..
FormBackingObjectMethod()---> #RequestMapping(requestMethod.GET)
while first time form shown to the screen formBackingObject is the reason
initBinder()---> normally used for suppose you want date field should be for example (custom date example : dd**MM***yyyy) needed means use initBinder method
onSubmit() -->#RequestMapping(requestMethod.POST)
while submitting the form onSubmit() method get called
i hope this helps

SpringMVC Command Object

I have a small doubt with SpringMVC's command object. So the problem is, I have couple of forms which I am going to use in multiple pages (Around 17). I decided to make these two forms as JSP's and include them in the parent JSP's. The problem I am facing is that the textfields and dropdown's aren't populating with the data which is passed on from the server using Command object.
Lets call the Parent JSP as parent.jsp and than two included JPS's(which have the forms) as child1.jsp and child2.jsp. The controller for parent.jsp sets a ModelAttribute with name previousSales and returns the view as abc/parent where abc is the folder. (WEB-INF/jsp/abc/parent). My question is how do I make the model attribute available to the included JSP's?
The Command object should be part of your handler method calls, as follows:
#RequestMapping("/somepath/foo/do")
public String someHandlerMethod(
#ModelAttribute("commandObject") CommandObject commandObject,
Model model,
BindingResult result) {
...processing...
return("some.jsp");
}
On your JSP, use the Spring Forms TLD for the form fields, and the Command object will be bound to the form fields.
#RequestMapping("/somepath/foo/do")
public String someHandlerMethod(
#ModelAttribute("commandObject") CommandObject commandObject,
Model model,
BindingResult result) {
...processing...
return("some.jsp");
}

Resources