0
votes

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?

1

1 Answers

0
votes

Spring did support nested properties binding. In your jsp, you are missing spring bind tag in some places. Try something like this.

<form:form method="POST" modelAttribute="registrationForm" commandName="registrationForm" class="col s12">
<input type="hidden" name="${_csrf.parameterName}"
       value="${_csrf.token}" />

<spring:bind path="user.email">
<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>
</spring:bind>

<div class="row">
    <spring:bind path="user.password">
    <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>
    </spring:bind>

    <spring:bind path="user.passwordConfirm">
    <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>
    </spring:bind>
</div>

<div class="row">
    <spring:bind path="student.surname">
    <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>
    </spring:bind>

    <spring:bind path="student.name">
    <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>
    </spring:bind>

    <spring:bind path="student.middlename">
    <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>
    </spring:bind>
</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>