I am having difficulties binding the spring form value to a backing object.
The following are the related parts of the code.
This is from page.jsp
<form:form method="post" commandName="building" action="addBuilding">
<div>
<div>
<form:label path="buildingName">Building Name:</form:label>
<form:input path="buildingName" />
<form:errors path="buildingName"></form:errors>
</div>
<div>
<form:label path="buildingType">Building Type:</form:label>
<form:select path="buildingType">
<form:option value="none">--Select One--</form:option>
<form:options items="${buildingTypeList}" itemValue="id" itemLabel="typeName"/>
</form:select>
<form:errors path="buildingType"></form:errors>
</div>
</div>
</form:form>
Model classes I want to bind are as the following. I add these for the sake of completeness
#Entity
#Table(name="tablename")
class Building {
#Column
private buildingName;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "buildingType", referencedColumnName = "id", nullable = false)
private BuildingType buildingType;
//other fields, getters and setters etc.
}
#Entity
#Table(name="tablename")
class BuildingType {
#Id
#Column
private int id;
#Column
private String typeName;
//getters, setters
}
At this point I can see the building type name in the combo-box just fine (in a GET request). The problem happens when I post the form. Itemvalue from combo-box is int and I want to bind it to the buildingType field in the Building model. The code will explain it better I guess. Related controller functions:
#RequestMapping(value = "addBuilding", method = RequestMethod.GET)
public String addBuildingPage(Model model) {
Building building = new Building();
model.addAttribute("building", building);
List<BuildingType> buildingTypeList = buildingTypeDao.findAll();
model.addAttribute("buildingTypeList", buildingTypeList);
return "addBuilding";
}
#RequestMapping(value = "addBuilding", method = RequestMethod.POST)
public String submitNewBuilding(#ModelAttribute(value = "building") #Valid Building building,
BindingResult result, Model model) {
if (result.hasErrors()) {
return "addBuilding";
}
model.addAttribute("building", building);
return "addBuilding";
}
I get a cannot cast int to BuildingType exception, after some search I followed the blog post written here. So I decided to write a custom Formatter and use ConversionService.
This is the formatter class
#Component
public class BuildingTypeFormatter implements Formatter<BuildingType> {
#Autowired
private BuildingTypeDao buildingTypeDao;
#Override
public String print(BuildingType buildingType, Locale arg1) {
return buildingType.getName();
}
#Override
public BuildingTypeDBO parse(String id, Locale arg1) throws ParseException {
return buildingTypeDao.findOne(Long.parseLong(id));
}
}
And this is the spring configuration class. (I don't use xml configuration.)
#EnableWebMvc
#Configuration
#ComponentScan({ "my.packages" })
public class MvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private BuildingTypeDBOFormatter formatter;
public MvcConfig() {
super();
}
#Bean(name = "conversionService")
public FormattingConversionServiceFactoryBean conversionService() {
FormattingConversionServiceFactoryBean bean = new FormattingConversionServiceFactoryBean();
Set<Formatter<?>> formatters = new HashSet<Formatter<?>>();
formatters.add(formatter);
bean.setFormatters(formatters);
return bean;
}
I think I need to register conversion service as explained in the blog post. Using and init binder in my controller like this.
#Autowired
ConversionService conversionService;
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setConversionService(conversionService);
}
The problem is I get the following exception when using setConversionException. And when I debug it I see that binder is initialized with a default conversionService.
java.lang.IllegalStateException: DataBinder is already initialized with ConversionService
at org.springframework.util.Assert.state(Assert.java:385)
at org.springframework.validation.DataBinder.setConversionService(DataBinder.java:562)
at my.package.controller.MyController.initBinder(MyController.java:138)
I came across with many answers suggesting setConversionService but it just doesn't work, how can I fix this? (PS: Sorry for the long post, but I think there may be couple of ways to fix this, so I preferred to post the whole thing.)
You can try add custom property editor in your controller
#InitBinder
public void initBinder(ServletRequestDataBinder binder) {
binder.registerCustomEditor(BuildingType.class, "buildingType", new PropertyEditorSupport() {
public void setAsText(String text) {
Long buildingTypeId = Long.parseLong(text);
BuildingType buildingType = (BuildingType) buildingTypeDao.findOne(buildingTypeId);
setValue(buildingType);
}
});
}
Related
I'm trying to add data to my database and reload the same page using spring boot and thymeleaf but when I save data I face this error
org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'org.closure.gcp.entities.QuestionEntity'; nested exception
is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Integer] for value 'adsf'; nested exception is java.lang.NumberFormatException: For input string: "adsf"
controller code :
#Controller
#RequestMapping(path = "/Questions")
public class QuestionView {
#Autowired
QuestionRepo questionRepo;
#RequestMapping(path = "/")
public String index(#ModelAttribute("question") QuestionEntity question, Model model)
{
List<QuestionEntity> list = questionRepo.findAll();
model.addAttribute("questions", list);
return "Questions";
}
#RequestMapping(value="/add", method=RequestMethod.POST)
public String addQuestion(Model model,#ModelAttribute("question") QuestionEntity question) {
questionRepo.save((QuestionEntity)model.getAttribute("question"));
List<QuestionEntity> list = questionRepo.findAll();
model.addAttribute("questions", list);
return "Questions";
}
}
thymeleaf page :
<html>
<header>
<title>Questions</title>
</header>
<body>
<h2>hello questions</h2>
<hr>
<tr th:each="q: ${questions}">
<td th:text="${q.question}"></td>
<br>
<td th:text="${q.question_type}"></td>
<hr>
</tr>
<!-- <form th:action="#{/add}" th:object="${question}" method="post"> -->
<form action="./add" th:object="${question}" method="POST">
<input type="text" th:field="*{question}" />
<br >
<input type="text" th:field="*{question_type}" />
<br >
<input type="submit" value="save" >
</form>
</body>
</html>
#Entity
#Table(name="question")
public class QuestionEntity {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Integer id;
#Column(nullable=false)
private String question;
#Column(nullable=false)
private String question_type;
#ManyToOne(optional = true)
private InterestEntity interest;
#ManyToOne(optional = true)
private LevelEntity level;
#Column(nullable = true)
private String sup_file;
#Column(nullable = false)
private int pionts;
#ManyToMany
private List<ContestEntity> contest;
#OneToMany(mappedBy ="question")
private List<AnswerEntity> answers;
// getters and setters
}
notice when I try to open another page in "/add" it works
I found this to solve
I just made a model class and use it instead of entity
and I used just one method to handle index and add requests
#Controller
#RequestMapping(path = "/Questions")
public class QuestionView {
#Autowired
QuestionRepo questionRepo;
#RequestMapping(path = {"/",""},method = {RequestMethod.POST,RequestMethod.GET})
public String index(#ModelAttribute("question") QuestionModel question, Model model,HttpServletRequest request)
{
if(request.getMethod().equals("POST"))
{
questionRepo.save(new QuestionEntity().question(question.getQuestion()).question_type(question.getQuestion_type()));
}
List<QuestionEntity> list = questionRepo.findAll();
model.addAttribute("questions", list);
return "Questions";
}
}
It is better to have 2 separate methods, one for GET and one for POST and to use redirect after the POST (see https://en.wikipedia.org/wiki/Post/Redirect/Get). This is how I would code this based on your separate QuestionModel class:
#Controller
#RequestMapping(path = "/Questions")
public class QuestionView {
#Autowired
QuestionRepo questionRepo;
#GetMapping
public String index(Model model)
{
List<QuestionEntity> list = questionRepo.findAll();
model.addAttribute("questions", list);
model.addAttribute("question", new QuestionModel());
return "Questions";
}
#PostMapping("/add")
public String addQuestion(#Valid #ModelAttribute("question") QuestionModel question, BindingResult bindingResult, Model model) {
if(bindingResult.hasErrors()) {
return "Questions";
}
questionRepo.save(new QuestionEntity().question(question.getQuestion()).question_type(question.getQuestion_type()));
return "redirect:/Questions";
}
}
Main points:
Use separate methods for GET and POST
Add the #Valid annotation to the #ModelAttribute in the POST method so any validation annotations on QuestionModel are checked (Because you probably want to make sure the question has at least some text in it for example).
Use BindingResult as parameter to check if there are validation errors.
Use "redirect:" to force a new GET after the POST to help avoid double submissions if a user would refresh the browser.
Error Details
The page works okay, but when I put incorrect period of time into two inputs startStatusDate and endStatusDate. Error occurred while validation check an error in class DriverHistoryValidator.
I see in debugger that exception generated after assignment
errors.rejectValue("startStatusDate",
"co.driverHistoryStatusPeriod.notpermitted")
in method
validate(Object o, Errors errors)
See below please
What am I doing wrong?
2018-05-03 11:03:54.364 ERROR 6234 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.beans.NotReadablePropertyException: Invalid property 'startStatusDate' of bean class [kg.nurtelecom.dictionaries.entity.carordering.Driver]: Bean property 'startStatusDate' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?] with root cause
org.springframework.beans.NotReadablePropertyException: Invalid property 'startStatusDate' of bean class [kg.nurtelecom.dictionaries.entity.carordering.Driver]: Bean property 'startStatusDate' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
Front End
Front end implemented in Thymeleaf:
<form id="statusSheduleForm" class="form-horizontal" th:action="#{/driver/saveStatusSchedule}"
th:method="POST"
th:object="${driverHistory}">
<div class="form-group col-md-7">
<div class="input-group date">
<label class="validation-message" for="statusdatetimepicker1"
th:if="${#fields.hasErrors('startStatusDate')}"
th:errors="*{startStatusDate}"></label>
<input type="text" placeholder="Время начала" th:field="*{startStatusDate}"
id="statusdatetimepicker1"/>
<input type="text" placeholder="Время окончания" th:field="*{endStatusDate}"
id="statusdatetimepicker2"/>
<select id="status-select" required="required" th:field="*{driverStatus}">
<option th:each="item:${statuses}"
th:value="${item.id}"
th:text="${item.name}"></option>
</select>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Сохранить</button>
<a th:href="#{/driver/index}" class="btn btn-default">Закрыть</a>
</div>
</form>
BackEnd:
Controller
#RequestMapping(value = "/saveStatusSchedule", method = RequestMethod.POST)
public ModelAndView saveStatusSchedule(#ModelAttribute DriverHistory driverHistory, Driver driver,
BindingResult bindingResult) {
ModelAndView modelAndView = getModelsViews();
Driver sessionDriver = (Driver) modelAndView.getModel().get("driver");
if (sessionDriver != null) {
sessionDriver.setMenu1Act();
driverHistory.setDriver(sessionDriver);
driverHistoryValidator.validate(driverHistory, bindingResult);
if (bindingResult.hasErrors()) {
return getModelsViews();
}
if (driverHistory.getDriverShift() != null) {
sessionDriver.setMenu2Act();
}
driverHistory.setDriver(sessionDriver);
driverHistoryService.save(driverHistory);
return getModelsViews();
} else {
driver.setMenu0Act();
modelAndView.addObject("failMessage", "Водитель не создан");
modelAndView.addObject("driver", driver);
return modelAndView;
}
}
Validator:
#Component
public class DriverHistoryValidator implements Validator {
#Autowired
DriverHistoryService driverHistoryService;
#Override
public boolean supports(Class<?> aClass) {
return DriverHistory.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
DriverHistory driverHistory = (DriverHistory) o;
if (driverHistoryService.isExistDriverStatusInPeriodOf(driverHistory)) {
errors.rejectValue("startStatusDate", "co.driverHistoryStatusPeriod.notpermitted");
}
}
}
Entity:
#Entity
#Table(name = "CO_DRIVER_HISTORY")
public class DriverHistory extends BaseEntity {
#DateTimeFormat(pattern = "dd.MM.yyyy HH:mm")
#Column(name = "startStatusDate")
private Date startStatusDate;
#DateTimeFormat(pattern = "dd.MM.yyyy HH:mm")
#Column(name = "endStatusDate")
private Date endStatusDate;
#DateTimeFormat(pattern = "dd.MM.yyyy HH:mm")
#Column(name = "startShiftDate")
private Date startShiftDate;
#DateTimeFormat(pattern = "dd.MM.yyyy HH:mm")
#Column(name = "endShiftDate")
private Date endShiftDate;
#ManyToOne
#JoinColumn(name = "DriverId")
private Driver driver;
#ManyToOne
#JoinColumn(name = "DriverStatusId")
private DriverStatus driverStatus;
#ManyToOne
#JoinColumn(name = "DriverShiftId")
private DriverShift driverShift;
public DriverHistory() {
}
public Date getStartStatusDate() {
return startStatusDate;
}
public void setStartStatusDate(Date startStatusDate) {
this.startStatusDate = startStatusDate;
}
public Date getEndStatusDate() {
return endStatusDate;
}
public void setEndStatusDate(Date endStatusDate) {
this.endStatusDate = endStatusDate;
}
public Date getStartShiftDate() {
return startShiftDate;
}
public void setStartShiftDate(Date startShiftDate) {
this.startShiftDate = startShiftDate;
}
public Date getEndShiftDate() {
return endShiftDate;
}
public void setEndShiftDate(Date endShiftDate) {
this.endShiftDate = endShiftDate;
}
public Driver getDriver() {
return driver;
}
public void setDriver(Driver driver) {
this.driver = driver;
}
public DriverStatus getDriverStatus() {
return driverStatus;
}
public void setDriverStatus(DriverStatus driverStatus) {
this.driverStatus = driverStatus;
}
public DriverShift getDriverShift() {
return driverShift;
}
public void setDriverShift(DriverShift driverShift) {
this.driverShift = driverShift;
}
}
I have figured out the root cause of issue. Let me answer to self asked question.
In my case the org.springframework.beans.NotReadablePropertyException generated since the property 'startStatusDate' is really not exists in class [kg.nurtelecom.dictionaries.entity.carordering.Driver]. Let me explain why it happened. Point is that the parameters of Controller's method mentioned below have incorrect sequence.
#RequestMapping(value = "/saveStatusSchedule", method = RequestMethod.POST)
public ModelAndView saveStatusSchedule(#ModelAttribute
DriverHistory driverHistory,
Driver driver,
BindingResult bindingResult)
BindingResult object declared in parameter obtains previous declared parameter as target object. See debug in screen shot. In my validator class DriverHistoryValidator I set error value to the field of class that is not target object. The issue has fixed after I simply changed the order of parameters in the method above like:
#RequestMapping(value = "/saveStatusSchedule", method = RequestMethod.POST)
public ModelAndView saveStatusSchedule(DriverHistory driverHistory,
BindingResult bindingResult,
Driver driver)
After the reorder of parameter, class DriverHistory is obtained as target object in BindinResult object and validation works properly.
First of all: I'm a beginner in Spring and this is my first try to implement an web application with Spring MVC.
Here is what I've done yet:
Entities:
#Entity
#Table(name = "coins")
public class Coin
{
#Id
#GeneratedValue
private Integer id;
#OneToOne
private Country country;
private double value;
private int year;
}
#Entity
#Table(name = "countries")
public class Country
{
#Id
#GeneratedValue
private Integer id;
private String name;
}
Controller:
#Controller
public class CoinViewController {
#Autowired
private CoinService service;
#Autowired
private CountryService countryService;
#ModelAttribute("countries")
public List<Country> frequencies() {
return countryService.get();
}
#RequestMapping(value = "/coins/add", method = RequestMethod.GET)
public String addCoin(Model model) {
model.addAttribute("coin", new Coin());
return "coins/add";
}
#RequestMapping(value = "/coins/add", method = RequestMethod.POST)
public String addCoinResult(#ModelAttribute("coin") Coin coin, BindingResult result) {
// TODO: POST HANDLING
return "/coins/add";
}
}
JSP:
<form:form action="add" method="POST" modelAttribute="coin">
<div class="form-group">
<label for="country">Country:</label>
<form:select path="country" class="form-control" >
<form:option value="" label="-- Choose one--" />
<form:options items="${countries}" itemValue="id" itemLabel="name" />
</form:select>
</div>
<div class="form-group">
<label for="value">Value:</label>
<form:input path="value" class="form-control" />
</div>
<div class="form-group">
<label for="year">Year:</label>
<form:input path="year" class="form-control" />
</div>
<button type="submit" value="submit" class="btn btn-default">Erstellen</button>
</form:form>
But when I try to save the input from the JSP I always get this:
Field error in object 'coin' on field 'country': rejected value [1];
codes
[typeMismatch.coin.country,typeMismatch.country,typeMismatch.Country,typeMismatch];
arguments
[org.springframework.context.support.DefaultMessageSourceResolvable:
codes [coin.country,country]; arguments []; default message
[country]]; default message [Failed to convert property value of type
'java.lang.String' to required type 'Country' for property 'country';
nested exception is java.lang.IllegalStateException: Cannot convert
value of type [java.lang.String] to required type [Country] for
property 'country': no matching editors or conversion strategy found]
So my questions are:
What should I use Editor / Converter?
How do I register one of them in my Controller?
You can register a custom editor into initBinder of your controller class:
#Controller
public class CoinViewController {
#Autowired
private CountryEditor countryEditor;
#InitBinder
protected void initBinder(final WebDataBinder binder, final Locale locale) {
binder.registerCustomEditor(Country.class, countryEditor);
}
......
}
(locale parameter is not needed in this case, but it can be useful if you need locale to make conversion - for example if you are working with dates)
and you can define your CountryEditor like the following:
#Component
public class CountryEditor extends PropertyEditorSupport {
#Autowired
private CountryService countryService;
#Override
public void setAsText(final String text) throws IllegalArgumentException {
try{
final Country country = countryService.findById(Long.parseLong(text));
setValue(cliente);
}catch(Exception e){
setValue(country);
// or handle your exception
}
}
}
I let spring handle injection of my editors with #Component annotation. So if you like to do in that way remember to enable package scan for that class!
Hope this help!
How to receive complex objects in spring 3.2 mvc?
In the simple example below, I have two model classes, with a many to one relationship. When adding a new Employee object, I would like to use a html select to select it's department.
When I post to add a new Employee, I get the following error:
Failed to convert property value of type java.lang.String to required type hu.pikk.model.Department for property department; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [hu.pikk.model.Department] for property department: no matching editors or conversion strategy found
How should I implement the editor or conversion strategy? Are there best practices, or pitfalls, one should watch for?
I've read the spring mvc documentation, and some articles and stackoverflow questions, but to be honest, I find them a little bit confusing and many times too short,too off-handed.
Models:
#Entity
public class Employee {
#Id
#GeneratedValue
private int employeeId;
#NotEmpty
private String name;
#ManyToOne
#JoinColumn(name="department_id")
private Department department;
//getters,setters
}
#Entity
public class Department {
#Id
#GeneratedValue
private int departmentId;
#Column
private String departmentName;
#OneToMany
#JoinColumn(name = "department_id")
private List<Employee> employees;
//getters,setters
}
In my controller class:
#RequestMapping(value = "/add", method = RequestMethod.GET)
private String addNew(ModelMap model) {
Employee newEmployee = new Employee();
model.addAttribute("employee", newEmployee);
model.addAttribute("departments", departmentDao.getAllDepartments());
return "employee/add";
}
#RequestMapping(value = "/add", method = RequestMethod.POST)
private String addNewHandle(#Valid Employee employee, BindingResult bindingResult, ModelMap model, RedirectAttributes redirectAttributes) {
if (bindingResult.hasErrors()) {
model.addAttribute("departments", departmentDao.getAllDepartments());
return "employee/add";
}
employeeDao.persist(employee);
redirectAttributes.addFlashAttribute("added_employee", employee.getName());
redirectAttributes.addFlashAttribute("message", "employee added...");
return "redirect:list";
}
In add.jsp:
<f:form commandName="employee" action="${pageContext.request.contextPath}/employee/add" method="POST">
<table>
<tr>
<td><f:label path="name">Name:</f:label></td>
<td><f:input path="name" /></td>
<td><f:errors path="name" class="error" /></td>
</tr>
<tr>
<td><f:label path="department">Department:</f:label></td>
<td><f:select path="department">
<f:option value="${null}" label="NO DEPARTMENT" />
<f:options items="${departments}" itemLabel="departmentName" itemValue="departmentId" />
</f:select></td>
<td><f:errors path="department" class="error" /></td>
</tr>
</table>
<input type="submit" value="Add Employee">
</f:form>
The problem is, when the controller receives the POST, it doesn't know how to convert the id string to a Department object.
I've found that there are basically three ways to solve this problem:
Not using spring form jstl, but a simple html select with a custom name, and in the Controller reading it with #RequestParam, accessing the database, and populating it.
Implementing the Converter interface, and registering it as a bean.
Implementing the PropertyEditor interface. Doing this is easier by extending the PropertyEditorSupport class.
I was going with the third option. (Later when I will have some time, I will edit this answer with the first two options explored.)
2. Implementing the Converter<String, Department> interface
#Component
public class DepartmentConverter implements Converter<String,Department>{
#Autowired
private DepartmentDao departmentDao;
#Override
public Department convert(String id){
Department department = null;
try {
Integer id = Integer.parseInt(text);
department = departmentDao.getById(id);
System.out.println("Department name:" + department.getDepartmentName());
} catch (NumberFormatException ex) {
System.out.println("Department will be null");
}
return department;
}
}
In the spring beans configuration file:
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="package.DepartmentConverter"/>
</list>
</property>
</bean>
3. Extending the PropertyEditorSupport class
public class SimpleDepartmentEditor extends PropertyEditorSupport {
private DepartmentDao departmentDao;
public SimpleDepartmentEditor(DepartmentDao departmentDao){
this.departmentDao = departmentDao;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
Department department = null;
try {
Integer id = Integer.parseInt(text);
department = departmentDao.getById(id);
System.out.println("Department name:" + department.getDepartmentName());
} catch (NumberFormatException ex) {
System.out.println("Department will be null");
}
setValue(department);
}
}
Inside the Controller, I needed to add an #InitBinder:
#InitBinder
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
binder.registerCustomEditor(Department.class, new SimpleDepartmentEditor(departmentDao));
}
For my opinion you've encounterd next situation here. When Spring trying to deserialize Employee entity which was received in addNewHandle it found property department of type of String but in target entity it has Department type, then, because you have no conversion regsieterd for such kind of conversion, it fails. So, to solve this issues you can implement own converter (Converter) that gets String and returns Department and regsiter it in conversionService or you can implement own JSON deserializing strategy by overriding Jackson JsonDeserializer and annotating department property with #JsonDeserialize(using=<your Jacson custom deserializer>.class). Hope this helps.
Why is spring not binding the values on my nested object?
The SecurityQuestion object on the RegistrationBean is set with question and answer as null, null, respectively, despite setting then in form using bean notation in the view.
Beans:
public class SecurityQuestion {
SecurityQuestionType type;
String answer;
}
public class RegistrationBean {
#Valid
SecurityQuestion securityQuestion;
String name;
public SecurityQuestionType[] getSecurityQuestionOptions() {
return SecurityQuestionType.values();
}
}
View:
<form:form modelAttribute="registrationBean" method="POST">
<form:select id="securityQuestion" path="securityQuestion.question">
<c:forEach var="securityQuestionOption" items="${securityQuestionOptions}">
<form:option value="${securityQuestionOption}">${securityQuestionOption</form:option>
</c:forEach>
</form:select>
<form:input id="securityAnswer" path="securityQuestion.answer" />
<form:input id="name" path="name" />
</form:form>
Controller:
#RequestMapping(value = URL_PATTERN, method = RequestMethod.POST)
public ModelAndView submit(#Valid final RegistrationBean registrationBean) {
// registrationBean.getSecurityQuestion().getQuestion() == null
// registrationBean.getSecurityQuestion().getAnswer() == null
}
Solution
All beans have to have getters/setters for all fields. Spring uses the default constructor and then uses the setters to mutate the object from the view.
Can you try giving the RegistrationBean an appropriate getter/setter.
public class RegistrationBean {
#Valid
SecurityQuestion securityQuestion;
String name;
public SecurityQuestion getSecurityQuestion(){
return securityQuestion;
}
public void setSecurityQuestion(SecurityQuestion q){
this.securityQuestion = q;
}
public SecurityQuestionType[] getSecurityQuestionOptions() {
return SecurityQuestionType.values();
}
}