如何在类级别约束之前验证字段级别约束?

时间:2015-05-25 04:34:38

标签: java bean-validation hibernate-validator jsr349

我有一个班级:

@ColumnNameUnique(groups = CreateTableChecks.class)
public class Table {    
    @Valid
    @NotEmpty(groups = CreateTableChecks.class)
    private List<Measure> measures; 
}
  • 类级别约束@ColumnNameUnique(groups = CreateTableChecks.class)始终先运行,然后运行字段级别约束@NotEmpty(groups = CreateTableChecks.class)

  • 无论如何强制首先运行字段级约束@NotEmpty(groups = CreateTableChecks.class)

2 个答案:

答案 0 :(得分:2)

您需要使用@GroupSequencere-define the default group sequence。如果没有这个,组中的验证顺序就不会被定义,它可以是任何顺序(在你的情况下,总是首先执行类级别约束不是必须的)。这样的事情应该有效:

@GroupSequence({FieldChecks.class, ClassChecks.class})
@ColumnNameUnique(groups = ClassChecks.class)
public class Table {    
    @Valid
    @NotEmpty(groups = FieldChecks.class)
    private List<Measure> measures; 
}

现在,如果@Default组得到验证,首先会验证类级别约束,然后验证字段级别约束。

答案 1 :(得分:-1)

您可以在@GroupSequence来电之前使用反射手动验证字段,而不是使用@Hardy提到的Validator.validate解决方案。

方式

您可以包装此方法

/**
 * Validates all single constrained fields of the given object and returns a
 * set of {@link ConstraintViolation}. If <code>first</code> is
 * <code>true</code> only the ConstraintViolation of the first invalid
 * constraint is returned. <br>
 * This method is useful to validate property constraints before class level
 * constraints.
 *
 * @param validator
 * @param object
 * @param first Set to <code>true</code> if only the exceptions of the first
 *            invalid property shall be thrown
 * @param groups
 */
public static Set<ConstraintViolation<Object>> validateProperties(final Validator validator, final Object object,
    final boolean first, final Class<?>... groups)
{
    if (object == null)
        throw new IllegalArgumentException("object must not be null.");
    if (validator == null)
        throw new IllegalArgumentException("validator must not be null.");

    final Set<ConstraintViolation<Object>> cvs = new HashSet<>();

    forFields: for (final Field field : ReflectionUtils.getAllFields(object.getClass(), null))
    {
        final Annotation[] annotations = field.getDeclaredAnnotations();

        boolean hasValidAnnotation = false;

        for (final Annotation annotation : annotations)
        {
            // single Constraint
            final Constraint constraint = annotation.annotationType().getAnnotation(Constraint.class);
            if (constraint != null)
            {
                cvs.addAll(validator.validateProperty(object, field.getName(), groups));

                if (!cvs.isEmpty() && first)
                    break forFields;
            }

            if (annotation.annotationType().equals(Valid.class))
                hasValidAnnotation = true;
        }

        // nested validation
        if (hasValidAnnotation)
        {
            field.setAccessible(true);
            Object value = null;
            try
            {
                value = field.get(object);
            }
            catch (IllegalArgumentException | IllegalAccessException e)
            {
                // log
            }

            if (value != null)
            {
                cvs.addAll(validateProperties(validator, value, first, groups));

                if (!cvs.isEmpty() && first)
                    break;

            }
        }
    }

    return cvs;
}

/**
 * Validates all single constrained fields of the given object and throws a
 * {@link ConstraintViolationException}. If <code>first</code> is
 * <code>true</code> only the ConstraintViolation of the first invalid
 * constraint is thrown. <br>
 * <br>
 * This method is useful to validate property constraints before class level
 * constraints.
 *
 * https://hibernate.atlassian.net/browse/BVAL-557
 *
 * @see #validateProperty(Validator, Object, String, Class...)
 *
 * @param validator
 * @param object
 * @param first Set to <code>true</code> if only the exceptions of the first
 *            invalid property shall be thrown
 * @param groups
 *
 * @throws ConstraintViolationException
 */
public static void validatePropertiesThrow(final Validator validator, final Object object, final boolean first,
    final Class<?>... groups) throws ConstraintViolationException
{
    if (object == null)
        throw new IllegalArgumentException("object must not be null.");
    if (validator == null)
        throw new IllegalArgumentException("validator must not be null.");

    final Set<ConstraintViolation<Object>> cvs = validateProperties(validator, object, first,
        groups);

    if (!cvs.isEmpty())
        throw new ConstraintViolationException(cvs);
}

我更喜欢这种方法,因为我不想用组序列注释更新所有实体和字段。