Pergunta

This will be a little complicated to explain, but please bear with me. I have a bean Student with several properties and I annotated all of the properties that need to be validated with Hibernate annotations. When I want to do a search for a student I only need some of the annotated/validated properties, but validation is done on all of them which doesn't allow for search to execute properly.

This is Student bean:

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "STUDENT_ID", unique = true, nullable = false)
    private Integer studentId;

    @NotNull(message = "First name is null!")
    @NotBlank(message = "Please enter first name!")
    @Column(name = "FIRST_NAME", nullable = false, length = 50)
    private String firstName;

    @NotNull(message = "Last name is null!")
    @NotBlank(message = "Please enter last name!")
    @Column(name = "LAST_NAME", nullable = false, length = 50)
    private String lastName;

    @NotNull(message = "MK is null!")
    @NotBlank(message = "Please enter MK!")
    @Column(name = "MK", nullable = false, length = 2)
    private String mk;

    @NotNull(message = "Roll number is null!")
    @NotBlank(message = "Please enter roll number!")
    @Column(name = "ROLL_NUMBER", nullable = false, length = 4)
    private String rollNumber;

    @NotNull(message = "Roll year is null!")
    @NotBlank(message = "Please enter roll year!")
    @Column(name = "ROLL_YEAR", nullable = false, length = 4)
    private String rollYear;

    //not populated by user, no validation needed
    @Column(name = "INDEX_NUMBER", unique = true, nullable = false, length = 20)
private String indexNumber;

    //for populating MK field only, no validation needed
    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch=FetchType.EAGER)
    @JoinColumn(name = "PROGRAM_FK", nullable = false)
    private Program program;

This is controller:

    @RequestMapping(value="/search", method = RequestMethod.POST)
    public String postSearchStudent(
            @RequestParam(value = "mk") String programId,
            @RequestParam(value = "rollNumber") String rollNumber,
            @RequestParam(value = "rollYear") String rollYear,
            @RequestParam(value = "indexNumber") String indexNumber,
            @Valid @ModelAttribute("searchStudentAttribute") Student student,
            BindingResult result,
            Model model) {

        logger.debug("Received request to search for a student");

        if (result.hasErrors()) {
            model.addAttribute("programList", programService.getAll());
            return "searchStudent";
        }
        else {
            student = studentService.search(indexNumber);
            // if student isn't in the db show add student page, else redirect to student page
            if (student == null) {
                student = new Student();
                student.setRollNumber(rollNumber);
                student.setRollYear(rollYear);
                student.setIndexNumber(indexNumber);
                Program program = programService.get(programId);
                student.setProgram(program);
                model.addAttribute("student", student);
                return "addStudent";
            }
            else {
                Integer studentId = student.getStudentId();
                model.addAttribute("studentId", studentId);
                return "redirect:/essays/main/student/{studentId}";
            }
        }
    }

This is view:

<c:url var="searchUrl" value="/essays/main/student/search" />
<form:form modelAttribute="searchStudentAttribute" method="POST" action="${searchUrl}">
 <form:errors path="*" cssClass="errorblock" element="div" />

 <form:label path="program">Select program (MK):</form:label>
 <form:select path="program" id="programSelect" size="8">
    <form:options items="${programList}" itemValue="programId" itemLabel="programDescription" />            
 </form:select>

 <form:label path="mk">MK</form:label>
 <form:input path="mk" id="mkInput" size="2" maxlength="2"/>
 <form:errors path="mk" cssClass="error"/>

 <form:label path="rollNumber">Roll number</form:label>
 <form:input path="rollNumber" id="rollNumberInput" size="4"/>
 <form:errors path="rollNumber" cssClass="error"/>

 <form:label path="rollYear">Roll year</form:label>
 <form:input path="rollYear" id="rollYearInput" size="4" maxlength="4"/>
 <form:errors path="rollYear" cssClass="error"/>

 <form:label path="indexNumber" />
 <form:hidden path="indexNumber" id="indexNumberInput" />

 <input type="submit" value="Submit" onclick="makeIndexNumber();"/>
</form:form>

As you can see, first name and last name of the student are not relevant for search, because the search is done by several other properties that are combined into one using javaScript. But I need first and last name to be validated later for, lets say, adding new student, so I can't just remove annotations and not validate those fields at all. Can someone please tell me should I make a separate bean for validation just to use for searching students, or there's some other proper solution for this?

Let me be more graphic:

enter image description here

When I submit form with all fields empty, I get these errors. How do I tell it not to take into consideration first and last name properties when search is done, without removing annotations for validation?

Foi útil?

Solução

This is a good scenario for using Validation Groups. To get an idea about validation groups check this link -

Validation Groups

The standard @Valid annotation still does not support groups. So, you can instead use Spring specific @Validated annotation along with validation groups.

@Validated

Outras dicas

The @NotNull annotation states that you can't assign a null-value to the annotated variable. I can't see you doing this anywhere in the code that you have provided, but here's what I think happens:

When you pass an object to Spring MVC, it creates it's own representation of the object, which is in turn made available to generated jsp-classes. When you submit a form, the transformation goes the other way, it recreates the original object, using it's set-methods. So, when you create a new Student, the firstName and lastName variables will be null, and that's fine. Then you pass it to Spring MVC, which translates the two variable values to null as well. But then, upon submitting the form, Spring MVC tries to translate back, and does setFirstName(null), and then there is trouble.

There are a couple of ways to deal with this situation.

  1. The easy one, just set the firstName and lastName variables to "" (empty string) before passing it to Spring MVC. Then it will never try to assign them with a null-value, it will be the empty string.
  2. The elegant one, create a new class StudentSearchCommand or something like that, which will act as a backing-object for the search dialog. This class should have the fields required for doing the seach, so everything Student has, except first and last name, but no validation, nor JPA-annotation (just a Plain Ol' Java Object (POJO)). Instantiate this class and send it to Spring MVC, and when the form is submitted, pick the values from it and fill a Student-object. In this way you can surpass the requirements that Spring MVC puts on your domain classes (like "must have a setter"), and keep them the way you want and intend them.
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top