1
votes

In JSR-303 (Bean Validation) you need to define a special annotation for each constraint validator you write. This makes perfect sense if you are creating reusable constraint validators (like the standard @Max, @NotNull, etc).

However in real life every validated bean requires its own validator to do more complex business validations. With vanilla JSR-303 implementations you have to create a separate annotation for every validator. This forces developer to write one-time-only annotations and makes the overall concept of bean validation look stupid. The necessity for one-time-only annotations can be avoided if JSR-303 offers some sort of delegating constraint annotation: @ValidateBy(validator=my.custom.Validator).

Now to my question:

  • Why doesn't JSR-303 include such use-case?
  • Is there any official discussion related to this (I was not able to find anything)?
  • Do any JSR-303 library offer such functionality (not that it would be hard to implement that)?

UPDATE 1 - Specific use-case (which led to this question)

We have a moderate enterprise application with pretty rich business model (40 manageable entities, 20 embeddable entities, 25 read-only entities). This means that we have a lots of HTML forms. Each form is backed by a designated form bean (70 form beans) with JSR-303 annotations. Some forms require custom non-trivial validation (e.g. if delivery type is email then a contact email must be set, ...). With JSR-303 we have 33 form-bean specific validators with 33 (unnecessary one-time-only) annotations.

With the number of Java classes (entities, controllers, DAOs, DTOs, mappers, validators, etc... right now this makes 800 .java files) I don't like having any boilerplate code around.

2
Good question, maybe they thought that the validation provided by other frameworks (like JSF) is enough.Kayaman

2 Answers

3
votes

Sometimes you need to ask the question to realize how to solve "the problem" yourself. Based on Gunnar's answer and comments:

You can create @MyDomainModelValid constriant definition for the custom domain model with all the necessary validators:

@Target({TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy={
        MyFirstEntityValidator.class, MySecondEntityValidator.class,
        MyThirdEntityValidator.class, EtCetera.class})
public @interface MyDomainModelValid {
    String message() default "entity.notValid";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default {};
}

JSR-303 implementation will make sure that a correct validator is called for a specific entity. So there is no need for previously proposed @ValidatedBy annotation.

2
votes

One of the core principles of Bean Validation is type-safety. Specific constraint annotations such as @Max, @Size allow to specify and access custom attributes such as the allowed maximum value in a type-safe safe manner.

The chosen approach also allows the validation engine to select the right validator implementation based on the annotated element's type instead of requiring the user to specify the validator class. So in a way this shifts complexity a bit from the constraint user to the constraint author.

As you say, implementing this as a custom constraint should not be difficult. Note though that this disables compile time checking of constraint correctness e.g. via the annotation processor provided by Hibernate Validator. While this could detect an erronously specified @Past constraint on a string property, it couldn't detect a non-matching validator type specified via @ValidatedBy.

If your requirement is about custom validation logic for complete beans (class-level validation), you might consider to implement that in a method of that bean as this:

@AssertTrue
public boolean isValid() {
    //custom validation logic
}

Or you might leverage the @ScriptAssert constraint provided by Hibernate Validator.