简体   繁体   中英

Spring MVC complex bean binding

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?

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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM