jax
jax

Reputation: 38593

Spring/Hibernate - validating groups manually and return a BindingResult

I want to manually validate (not using @Valid or @Validated) using groups and return a BindingResult.

I have a spring validator configured

  <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" >
    <property name="validationMessageSource" ref="messageSource"/>
  </bean> 

And this is the class that does the validation, it works but notice that groups is not being used.

@Component
public class ObjectValidatorImpl implements ObjectValidator {
    private final Validator validator;

    @Autowired
    public ObjectValidatorImpl(final Validator validator) {
        super();
        this.validator = validator;
    }

    @Override
    public final <T> BindingResult getBindingResults(
               final T objectToValidate, final Class<?>...groups) {
        final DataBinder binder = new DataBinder(objectToValidate);
        binder.setValidator(validator);

        //ideally, I would like to set the groups on the binder here like
        //binder.setGroups(groups);

        binder.validate();
        return binder.getBindingResult();
    }
}

I cant figure out how to get the Validator to use the groups, the seems to be no method for it.

I would like to call it like this.

objectValidator.validate(myObject, Class1.class, Class2.class)

Upvotes: 4

Views: 3471

Answers (3)

Grigory Kislin
Grigory Kislin

Reputation: 18010

Since 3.1 version there are org.springframework.validation.DataBinder#validate(java.lang.Object...validationHints)

So just do:

binder.validate(Class1.class, Class2.class);

Inside method SmartValidator is used:

if(... validator instanceof SmartValidator) {
   ((SmartValidator) validator).validate(target, bindingResult, validationHints);

Upvotes: 0

Marcelo Barros
Marcelo Barros

Reputation: 1048

I think the easiest way is to use a SmartValidator. You can inject it just like a regular Validator. By using a SmartValidator you have Validation Hints, which you can use to pass the javax.validation.group as parameters. And the final code can be as simple as this:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.SmartValidator;

@Component
public class FooController {

    @Autowired
    private SmartValidator validator;

    public void validate(MyObject target, Errors errors) {
        validator.validate(target, errors, FirstCheck.class, SecondCheck.class);
    }
}

Upvotes: 1

John Farrelly
John Farrelly

Reputation: 7459

I had a similar issue, and ended up extending the SpringValidatorAdapter class so that I could perform the validation and specify the groups.

ExtendedSpringValidatorAdapter adapter = new ExtendedSpringValidatorAdapter(validator);
adapter.validate(objectToValidate, bindingResult, Class1.class, Class2.class);

Here is the definition of the extended validator:

package com.example.validator;

import org.springframework.beans.NotReadablePropertyException;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.groups.Default;
import java.util.Set;

public class ExtendedSpringValidatorAdapter extends SpringValidatorAdapter {

    // ========================================================================
    // ========================================================================
    // ========================================================================
    public ExtendedSpringValidatorAdapter(Validator targetValidator) {
        super(targetValidator);
    }

    // ========================================================================
    // ========================================================================
    // ========================================================================
    public void validate(Object target, Errors errors, Class<?>... groups) {

        if (groups == null || groups.length == 0 || groups[0] == null) {
            groups = new Class<?>[]{Default.class};
        }

        Set<ConstraintViolation<Object>> result = validate(target, groups);
        for (ConstraintViolation<Object> violation : result) {
            String field = violation.getPropertyPath().toString();
            FieldError fieldError = errors.getFieldError(field);
            if (fieldError == null || !fieldError.isBindingFailure()) {
                try {
                    errors.rejectValue(field,
                            violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName(),
                            getArgumentsForConstraint(errors.getObjectName(), field, violation.getConstraintDescriptor()),
                            violation.getMessage());
                } catch (NotReadablePropertyException ex) {
                    throw new IllegalStateException("JSR-303 validated property '" + field +
                            "' does not have a corresponding accessor for Spring data binding - " +
                            "check your DataBinder's configuration (bean property versus direct field access)", ex);
                }
            }
        }
    }
}

Upvotes: 3

Related Questions