Аннотация @Valid не проверяет список дочерних объектов

Основные классы моделей следующие:

public class UserAddressesForm {

    @NotEmpty
    private String firstName;

    @NotEmpty
    private String lastName;

    private List<AddressForm> addresses;

    // setters and getters 

}

public class AddressForm {
    
    @NotEmpty
    private String customName;
    @NotEmpty
    private String city;
    @NotEmpty
    private String streetAn;
    @NotEmpty
    private String streetHn;
    @NotEmpty
    private String addressCountry;
    @NotEmpty
    private String postCode;
    
    // setters and getters
}

Конечная точка в одном из моих контроллеров:

@RequestMapping(value = "/up", method = RequestMethod.POST)
public String completeForm(@Valid @ModelAttribute("userAddressesForm") UserAddressesForm userAddressesForm,  
            BindingResult result, HttpServletRequest req) {

 // logic here 

}

Страница .jsp:

<form:form commandName="userAddressesForm" action="registered">
    <table>

        <tr>
            <td class="formLabels"><form:label path="firstName">
                <spring:message code="label.name" />
            </form:label></td>
            <td><form:input path="firstName" /></td>
            <td><form:errors path="firstName" cssClass="error" /></td>
        </tr>
        <tr>
            <td class="formLabels"><form:label path="lastName">
                <spring:message code="label.surname" />
            </form:label></td>
            <td><form:input path="lastName" /></td>
            <td><form:errors path="lastName" cssClass="error" /></td>
        </tr>
    </table>
    
    <c:forEach items="${userAddressesForm.addresses}" varStatus="gridRow">  
        <div id="main_address" class="address_data_form">
            <fieldset>
                <legend><spring:message code="label.stepThreeMainAddressInfo" /></legend>
                <a href="#" class="deleteItem"></a>
                <table>
                    <tr>            
                        <td class="formLabels">
                            <spring:message code="label.address.custom.name" />
                        </td>
                        <td>
                            <spring:bind path="addresses[${gridRow.index}].customName">
                                <input type="input" name="<c:out value="${status.expression}"/>"
                                    id="<c:out value="${status.expression}"/>"
                                    value="<c:out value="${status.value}"/>" />
                                    <form:errors path="${status.expression}"/>
                            </spring:bind>
                        </td>   
                    </tr>               
                    <tr>            
                        <td class="formLabels">
                            <spring:message code="label.streetAnStreetHn" />
                        </td>
                        <td>
                            <spring:bind path="addresses[${gridRow.index}].streetAn">
                                <input type="input" name="<c:out value="${status.expression}"/>"
                                    id="<c:out value="${status.expression}"/>"
                                    value="<c:out value="${status.value}"/>" />
                            </spring:bind>
                            <spring:bind path="addresses[${gridRow.index}].streetHn">
                            <input type="input" name="<c:out value="${status.expression}"/>"
                                id="<c:out value="${status.expression}"/>"
                                value="<c:out value="${status.value}"/>" >
                            <form:errors path="addresses[${gridRow.index}].streetHn"/>
                            </spring:bind>
                            
                        </td>
                    </tr>
                    <tr>                        
                        <td class="formLabels">
                            <spring:message code="label.postCode" />
                        </td>
                        <td>
                            <spring:bind path="addresses[${gridRow.index}].postCode">
                                <input type="input" name="<c:out value="${status.expression}"/>"
                                    id="<c:out value="${status.expression}"/>"
                                    value="<c:out value="${status.value}"/>" />
                            </spring:bind>
                        </td>                   
                    </tr>
                    <tr>                
                        <td class="formLabels">
                            <spring:message code="label.city" />
                        </td>
                        <td>
                            <spring:bind path="addresses[${gridRow.index}].city">
                                <input type="input" name="<c:out value="${status.expression}"/>"
                                    id="<c:out value="${status.expression}"/>"
                                    value="<c:out value="${status.value}"/>" />
                                <form:errors path="addresses[${gridRow.index}].city" cssClass="error" />
                            </spring:bind>
                        </td>
                    </tr>       
                </table>    
            </fieldset>
        </div>
    </c:forEach>

Почему @Valid не проверяет List<AddressForm> addresses, присутствующий в классе UserAddressesForm?


person tomaszd    schedule 28.02.2011    source источник


Ответы (4)


Вам нужно украсить addresses член UserAddressesForm аннотацией @Valid. См. Разделы 3.1.3 и 3.5.1 документа JSR 303: Bean Validation. Как я объяснил в своем ответе на вопрос Существует ли стандартный способ включения проверки компонентов JSR 303 с использованием аннотированного метода, это реальное использование аннотации @Valid согласно JSR 303.

Изменить Пример кода: Hibernate Validator - Object Graph. (Список пассажиров в Автомобиле)

Изменить из Hibernate Validator 6 Справочный документ:

В версиях до 6 Hibernate Validator поддерживал каскадную проверку для подмножества элементов контейнера, и это было реализовано на уровне контейнера (например, вы могли бы использовать @Valid private List<Person>, чтобы включить каскадную проверку для Person).

Это все еще поддерживается, но не рекомендуется. Используйте вместо них аннотации @Valid уровня контейнерных элементов, поскольку они более выразительны.

Пример:

public class Car {

        private List<@NotNull @Valid Person> passengers = new ArrayList<Person>();

        private Map<@Valid Part, List<@Valid Manufacturer>> partManufacturers = new HashMap<>();

       //...
   }

Также ознакомьтесь с новыми возможностями Bean Validation 2.0 / Jakarta Bean Validation.

person Ritesh    schedule 28.02.2011
comment
А как насчет группировки? Я хочу, чтобы адрес проверялся только тогда, когда проверяется группа А. @Valid игнорирует группу. - person Akshay; 03.01.2017
comment
Аннотация @Akshay @Valid работает на нижнем уровне. Я думаю, что @ GroupSequence можно использовать для указания порядка проверки. - person Ritesh; 03.01.2017
comment
Мой подробный вопрос stackoverflow.com/q/41457404/1534925. Не думаю, что последовательность сработает в моем случае. - person Akshay; 04.01.2017

Добавление к ответу @Ritesh ограничение @Valid даст указание Bean Validator углубиться в тип применяемого свойства и проверить все ограничения, найденные там. Ответьте кодом на ваш вопрос, валидатор, увидев ограничение @Valid для свойства addresses, исследует класс AddressForm и проверит все ограничения JSR 303, обнаруженные внутри, следующим образом:

public class UserAddressesForm {

    @NotEmpty
    private String firstName;

    @NotEmpty
    private String lastName;

    @Valid
    private List<AddressForm> addresses;

...
setters and getters 

public class AddressForm {

    @NotEmpty
    private String customName;
    @NotEmpty
    private String city;
    @NotEmpty
    private String streetAn;
    @NotEmpty
    private String streetHn;
    @NotEmpty
    private String addressCountry;
    @NotEmpty
    private String postCode;
...
setters and getters
person Arpit Aggarwal    schedule 05.10.2016
comment
А как насчет группировки? Я хочу, чтобы адрес проверялся только тогда, когда проверяется группа А. @Valid игнорирует группу - person Akshay; 03.01.2017
comment
Я даже не прочитал ваш ответ, но просто просмотр setters and getters решил мою проблему. - person Shubham Khandare; 10.07.2020

В классе UserAddressesForm добавьте следующие строки

@Valid
private List<AddressForm> addresses;
person Tapan Banker    schedule 16.02.2019

Рабочий раствор.

public class UserAddressesForm {

    @NotEmpty(message="firstName is required")
    private String firstName;

    @NotEmpty(message="lastNameis required")
    private String lastName;

    @NotNull(message="addresses attributes are required")
    @Valid
    private List<AddressForm> addresses;

...
setters and getters 

public class AddressForm {

    @NotEmpty(message="customNameis required")
    private String customName;
    @NotEmpty
    private String city;
    @NotEmpty
    private String streetAn;
    @NotEmpty
    private String streetHn;
    @NotEmpty
    private String addressCountry;
    @NotEmpty
    private String postCode;
...
setters and getters
person Waqas Ahmed    schedule 26.04.2019