Search code examples
validationjava-ee-6

Define custom validation annotation that extends an existing one - javaEE


I want to define a custom @DateOfBirthValid validation annotation that makes use of @Pattern annotation so that I define the date of birth pattern once and use it wherever ... I know I can define it using @Constraint(validatedBy = SomeClass.class) then define the isValid() method inside SomeClass but I wonder if there is a more direct way to use the @Pattern annotation and give it the date of birth regex I want ... the reason is that I want to use the same validation annotation in many places in the code without defining the pattern again


Solution

  • From https://docs.oracle.com/javaee/6/tutorial/doc/gkfgx.html:

    Bean Validation includes several built-in constraints that can be combined to create new, reusable constraints. This can simplify constraint definition by allowing developers to define a custom constraint made up of several built-in constraints that may then be applied to component attributes with a single annotation.

    In the example in the link you can see how @Pattern is used (amongst others) to create an Email validation annotation:

    @Pattern.List({
      @Pattern(regexp = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\."
        +"[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*"
        +"@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
    })
    @Constraint(validatedBy = {})
    @Documented
    @Target({ElementType.METHOD,
        ElementType.FIELD,
        ElementType.ANNOTATION_TYPE,
        ElementType.CONSTRUCTOR,
        ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Email {
    
        String message() default "{invalid.email}";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
        @Target({ElementType.METHOD,
            ElementType.FIELD,
            ElementType.ANNOTATION_TYPE,
            ElementType.CONSTRUCTOR,
            ElementType.PARAMETER})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @interface List {
            Email[] value();
        }
    }
    

    So from my (rather limited) understanding of this you should be able to create a new annotation similar to this

    @Pattern(regexp = yourRegex)
    @Documented
    @Target(/* all targets here */)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DateOfBirthValid {
        /* all methods required */
        /* Same thing for the List interface */
    }
    

    Which then should be usable like

    @DateOfBirthValid
    protected String myString
    

    or for the example from the link

    This custom constraint can then be applied to an attribute.

    ...
    @Email
    protected String email;
    ...