I've went thru Spring documentation and source code and still haven't found answer to my question.
I have these classes in my domain model and want to use them as backing form objects in spring-mvc.
public abstract class Credentials {
private Long id;
....
}
public class UserPasswordCredentials extends Credentials {
private String username;
private String password;
....
}
public class UserAccount {
private Long id;
private String name;
private Credentials credentials;
....
}
My controller:
@Controller
public class UserAccountController
{
@RequestMapping(value = "/saveAccount", method = RequestMethod.POST)
public @ResponseBody Long saveAccount(@Valid UserAccount account)
{
//persist in DB
return account.id;
}
@RequestMapping(value = "/listAccounts", method = RequestMethod.GET)
public String listAccounts()
{
//get all accounts from DB
return "views/list_accounts";
}
....
}
On UI I have dynamic form for the different credential types. My POST request usually looks like:
name name
credentials_type user_name
credentials.password password
credentials.username username
Following exception is thrown if I try to submit request to the server :
org.springframework.beans.NullValueInNestedPathException: Invalid property 'credentials' of bean class [*.*.domain.UserAccount]: Could not instantiate property type [*.*.domain.Credentials] to auto-grow nested property path: java.lang.InstantiationException
org.springframework.beans.BeanWrapperImpl.newValue(BeanWrapperImpl.java:628)
My initial thought was to use @ModelAttribute
@ModelAttribute
public PublisherAccount prepareUserAccountBean(@RequestParam("credentials_type") String credentialsType){
UserAccount userAccount = new PublisherAccount();
Class credClass = //figure out correct credentials class;
userAccount.setCredentials(BeanUtils.instantiate(credClass));
return userAccount;
}
Problem with this approach is that prepareUserAccountBean
method get called before any other methods (like listAccounts
) as well which is not appropriate.
One robust solution is to move out both prepareUserAccountBean
and saveUserAccount
to the separate Controller. It doesn't sound right : I want all user-related operations to reside in the same controller class.
Any simple solution? Can I utilize somehow DataBinder, PropertyEditor or WebArgumentResolver?
Thank you!!!!!
Data binding is useful for allowing user input to be dynamically bound to the domain model of an application (or whatever objects you use to process user input). Spring provides the so-called DataBinder to do exactly that.
In the last section, we saw how to bind data submitted by an HTML form or by query string parameters to a form-backing bean. In order to do the binding, Spring MVC internally uses a special binding object called WebDataBinder ( org. springframework.
Formatters are used to convert String to another Java type and back. So, one type must be String . You cannot, for example, write a formatter that converts a Long to a Date . Examples of formatters are DateFormatter , for parsing String to java. util.
(sprɪŋ ˈbaɪndɪŋ ) a method of securing loose sheets of paper in a binder, using a mechanism containing a metal spring.
I can't see any simple and elegant solution. Maybe because the problem is not how to data bind abstract classes in Spring MVC, but rather : why having abstract classes in form objects in the first place ? I think you shouldn't.
An object sent from the form to the controller is called a "form (backing) object" for a reason : the object attributes should reflect the form fields. If your form has username and password fields, then you should have username and password attributes in your class.
So credentials
should have a UserPasswordCredentials
type. This would skip your "abstract instantiation attempt" error. Two solutions for this :
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With