One unsolved problem with Bean Validation is how to apply a constraint on the elements of a collection (or iterable). Think about a User entity with a list of emails (primary, secondary, ...). This could be modeled like this:
public class User { @NotEmpty private String name; @NotEmpty @Email private List<String> emailList; ... }
The problem is that validating an instance of this User would currently throw an UnexpectedTypeException, since there is no built-in ConstraintValidator<Email, Collection<String>> which could validate emailList (see also HV-264).
One way to solve the problem within the current framework is to implement a ConstraintValidator<Email, Iterable<String>>. Great, but it only solves the problem for @Email, not for the generic problem: How can I apply a constraint on all elements of a collection?
.
Not thinking about language limitations, the following solution would be nice:
public class User { @NotEmpty private String name; @NotEmpty private List<@Email String> emailList; ... }
Of course this is not possible in the current version of Java. However, there is JSR 308, which allows the above example and which will hopefully be part Java 7. For the Bean Validation specification it makes sense to wait for this feature (see BVAL-202), but should we have a interim solution for Hibernate Validator. This is what HV-296 is about. HV-296 discusses several solutions to the problem. Two solutions I would like to introduce here:
The payload solution
For this solution a custom payload class would be introduced. If this payload is specified on a constraint, the constraints will be applied on the elements of the collection:
public class User { @NotEmpty private String name; @NotEmpty @Email(payload=OnElements.class) private List< String> emailList; ... }
This seems to be the least intrusive solution and hopefully within the rules of the specification: Payloads are typically used by validation clients to associate some metadata information with a given constraint declaration. Payloads are typically non-portable
.
The @ValidateEach annotation solution
For this solution we would introduce a new annotation @ValidateEach (or maybe @ApplyOn).
@ValidateElements public @interface ValidateEach { Email[] value(); }
@ValidateElements is a marker annotation used by the validator engine to determine annotations used for applying built-in constraints on collections. Our usecase looks like this now:
public class User { @NotEmpty private String name; @NotEmpty @ValidateEach(@Email) private List< String> emailList; ... }
Problematic with this solution is that for each constraint we have to place the corresponding ValidateEach annotation in its own package. This could be avoided if we give each annotation a distinctive name, for example ValidateEachEmail, ValidateEachSize, etc. Alternatively we could have one single ValidateEach annotation and attributes for each built-in constraint:
@ValidateElements public @interface ValidateEach { Email[] email() default{}; Size[] size() default {}; NotNull[] notNull() default {}; ... }
This would reduce the required number of annotations, but it would introduce a special case for custom constraints.
You get the full history on how we arrived at these solutions by reading the irc log attached to HV-296. If you have an opinion on the matter, make sure to leave a comment, post on the forum or send an email to hibernate-dev.