I have transfer object that contains two beans: Student and User.
@Entity
@Table(name = "student")
public class Student implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "middleName")
private String middleName;
@Column(name = "surname")
private String surname;
@ManyToOne
@JoinColumn(name = "idSpeciality")
private Speciality speciality;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public Speciality getSpeciality() {
return speciality;
}
public void setSpeciality(Speciality speciality) {
this.speciality = speciality;
}
}
@Entity
@Table(name = "users")
public class User implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "email")
private String email;
@Column(name = "password")
private String password;
@Transient
private String passwordConfirm;
@ManyToMany
@JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"))
private Set<Role> role;
public String getPasswordConfirm() {
return passwordConfirm;
}
public void setPasswordConfirm(String passwordConfirm) {
this.passwordConfirm = passwordConfirm;
}
public Set<Role> getRole() {
return role;
}
public void setRole(Set<Role> roles) {
this.role = roles;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Transfer object:
public class RegistrationTO implements Serializable{
private User user;
private Student student;
public RegistrationTO() {
user = new User();
student = new Student();
student.setSpeciality(new Speciality());
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
}
Also I have registration page with form:
<form:form method="POST" modelAttribute="registrationForm" commandName="registrationForm" class="col s12">
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}" />
<div class="row">
<div class="input-field col s12 ${status.error ? 'has-error' : ''}">
<i class="material-icons prefix">email</i>
<form:input type="email" path="user.email" class="validate"
placeholder="Email" id="loginInput" autofocus="true" />
<form:errors path="user.email"></form:errors>
<label for="loginInput">Email</label>
</div>
</div>
<div class="row">
<div class="input-field col s6 ${status.error ? 'has-error' : ''}">
<i class="material-icons prefix">vpn_key</i>
<form:input type="password" path="user.password" class="form-control"
placeholder="Пароль" id="passwordInput"></form:input>
<form:errors path="user.password"></form:errors>
<label for="passwordInput">Пароль</label>
</div>
<div class="input-field col s6 ${status.error ? 'has-error' : ''}">
<form:input type="password" path="user.passwordConfirm" class="form-control"
placeholder="Повторите пароль" id="secondPasswordInput"></form:input>
<form:errors path="user.passwordConfirm"></form:errors>
<label for="secondPasswordInput">Повторите пароль</label>
</div>
</div>
<div class="row">
<div class="input-field col s4 ${status.error ? 'has-error' : ''}">
<i class="material-icons prefix">account_circle</i>
<form:input type="text" path="student.surname" class="validate"
placeholder="Фамилия" id="stSurnameInput"></form:input>
<form:errors path="student.surname"></form:errors>
<label for="stSurnameInput">Фамилия</label>
</div>
<div class="input-field col s4 ${status.error ? 'has-error' : ''}">
<form:input type="text" path="student.name" class="validate"
placeholder="Имя" id="stNameInput"></form:input>
<form:errors path="student.name"></form:errors>
<label for="stNameInput">Имя</label>
</div>
<div class="input-field col s4 ${status.error ? 'has-error' : ''}">
<form:input type="text" path="student.middleName" class="validate"
placeholder="Отчество" id="stMiddleNameInput"></form:input>
<form:errors path="student.middleName"></form:errors>
<label for="stMiddleNameInput">Отчество</label>
</div>
</div>
<div class="input-field">
<i class="material-icons prefix">list</i>
<select name = "university" id="university">
<option value="" selected>Choose your option</option>
<c:forEach var="univer" items="${universities}">
<option value="${univer.id}">${univer.name}</option>
</c:forEach>
</select>
<label for="university">Выберите университет</label>
</div>
<div class="input-field">
<i class="material-icons prefix">list</i>
<select id="faculty">
<option value="" selected>Choose your option</option>
</select>
<label for="faculty">Выберите факультет</label>
</div>
<div class="input-field">
<i class="material-icons prefix">list</i>
<form:select path="student.speciality.id" id="speciality">
<option value="" selected>Choose your option</option>
</form:select>
<label for="speciality">Выберите специальность</label>
</div>
<button class="btn waves-effect waves-light right" type="submit" name="action">Принять
<i class="material-icons right">send</i>
</button>
</form:form>
Controller for get and post requests:
@RequestMapping(value = "/registration", method = RequestMethod.GET)
public ModelAndView registration(@ModelAttribute("registrationForm") RegistrationTO registrationForm,
ModelMap modelMap) {
ModelAndView modelAndView = new ModelAndView("registrationTemplate");
if (registrationForm == null) {
registrationForm = new RegistrationTO();
}
modelAndView.addObject("registrationForm", registrationForm);
modelAndView.addObject("universities", universityService.getAllUniversities());
//modelAndView.addObject(BindingResult.MODEL_KEY_PREFIX + "registrationForm", modelMap.get("error"));
return modelAndView;
}
@RequestMapping(value = "/registration", method = RequestMethod.POST)
public String registration(@ModelAttribute("registrationForm") RegistrationTO registrationForm,
BindingResult bindingResult,
RedirectAttributes attributes) {
User userForm = registrationForm.getUser();
Student studentForm = registrationForm.getStudent();
userValidator.validate(userForm, bindingResult);
/*if (bindingResult.hasErrors()) {
attributes.addFlashAttribute("error", bindingResult);
attributes.addFlashAttribute("userForm", userForm);
return "redirect:/registration";
}*/
userService.createUser(userForm);
securityService.autologin(userForm.getEmail(), userForm.getPasswordConfirm());
return "redirect:/index";
}
After filling fields, I click submit button and get next message: "HTTP Status 500 - Request processing failed; nested exception is org.springframework.beans.NotReadablePropertyException: Invalid property 'email' of bean class [by.bsuir.ceres.bean.TO.RegistrationTO]: Bean property 'email' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?"
It looks like spring doesn't search properties in nested beans, but why?