2015-05-25 1 views
2

У меня есть класс:Как проверить ограничение уровня на уровне до ограничения уровня класса?

@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)?

ответ

1

Вы должны использовать @GroupSequence и re-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

Это поведение должно быть по умолчанию. Если простые поля не проверяются до того, как весь бит проверен, мы должны либо использовать обходной путь, как это, либо перепроверить поля в валидаторе классов (проверить свойства, уже отмеченные как '@ NotNull'). Боб никогда не может быть действительным, если проверка не выполняется в любом отдельном поле. – djmj

+1

Я создал проблему на https://hibernate.atlassian.net/browse/BVAL-557. Единственное общее и практическое решение, которое я придумал, - это сначала проверять отдельные свойства, используя отражение, вместо того, чтобы добавлять это обходное решение ко всем нашим объектам. – djmj

-1

Вместо использования @Hardy упомянутого решения с @GroupSequence вы можете проверить поля вручную, используя отражение перед вызовом 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); 
} 

Я предпочитаю такой подход, так как я не хочу, чтобы обновить все наши объекты и поля с аннотациями группы последовательностей.