Can I add a bean validation annotation with group? - java

I want to build a NotNull check that only work on Create group, so I can write like this
#Data
public static class TestDto {
#NotNullOnCreate
// #NotNull(groups = Create.class) // instead of this
private String id;
}
I create the NotNullOnCreate like this
#Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
#Retention(RUNTIME)
#Documented
#Repeatable(NotNullOnCreate.List.class)
#ReportAsSingleViolation
#Constraint(validatedBy = {})
#NotNull(groups = Create.class)
#interface NotNullOnCreate {
String message() default "{javax.validation.constraints.NotNull.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
#Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
#Retention(RUNTIME)
#Documented
#interface List {
NotNullOnCreate[] value();
}
}
This doesn't work, because composing constraint annotation's (see ConstraintDescriptorImpl#createComposingConstraintDescriptor) group will be set to host annotation's group('Default').
I also tried this Class<?>[] groups() default {Create.class};, but this is not allowed, the default group must empty.
How can I achieve this ? I don't want to write the group everywhere.
EDIT
Create HV-1355 Constraint annotation with explicit groups
Wrote a workable demo project hv-annotation-with-group

This is not possible at the moment. If you like, open an issue in the Hibernate Validator issue tracker and we can explore this feature.

Related

Can I create custom validation annotation that inherits from javax.constrains annotations

I'm wondering if it's possible to do something like:
#Min(1)
#Max(100)
public #interface ValidationForX {}
and then
#ValidationForX
int X;
For some reason #Min and #Max are applicable on annotations so I'm assuming it should be possible
I want to hide this validation behind one annotation because I want to reuse it
Thanks for your help!
You annotation must look like this:
#Min(1)
#Max(100)
#Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = {})
public #interface ValidationForX {
String message() default "value should be greater or equal than 1 and less or equal than 100.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Read more about composing constraints here: https://www.baeldung.com/java-bean-validation-constraint-composition
You can directly use #Min and #Max annotation on top of the field which you want to validate.
Something like this:
#Max(value=100)
#Min(value=1)
int X;

Changing default group in custom constraint annotations

I have a working custom class level constraint that validates all annotations that do not belong in a group. How can I change that to validate a specific group with that constraint?
This is the annotation class I have:
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = CheckCase.class)
#Documented
public #interface ValidName {
String message() default "{message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Documented
#interface List {
ValidName[] value();
}
}
Right now, the validation works only if I use:
#CheckCase(message = "Error message")
I would like to use this:
#CheckCase(message = "Error message", groups = CheckCaseGroup.class)
Do I have to change something in the following line?
Class<?>[] groups() default {};
Separate question:
If I run the validator without passing in a group, will it go through all validations including the ones that have a group, or will it go only through the ones that don't have a specified group? For example:
will running this:
validator.validate(inputData);
go through this annotations:
#NotBlank(groups = CheckCaseGroup.class)
or only this:
#NotBlank()

How to order Bean Validation

I have create a validation annotation to validate the content of a string. The definition of my annotation looks like this:
#NotNull
#Size(min = 2, max = 128, groups = First.class)
#Pattern(regexp = "^(?!WP_([0-9])+$)[A-Z_][A-Z0-9_-]+", groups = Second.class)
#Target({FIELD, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = {})
#GroupSequence(value={First.class, Second.class})
public #interface ValidIntentName {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
My problem is that i cannot order the #Size and #Pattern execution. I've tried a couple of things but no luck so far.
Basically I need #Size to be executed before #Pattern
First and Second interfaces are defined. There are no errors during compilation is just that during run-time #Patter is executed before #Size.
Any idea what I'm doing wrong?
I'm running Java 8.
Thanks

Combining custom constraint validator annotation

So I have created a custom constraint validator annotation like so:
#Documented
#Constraint(validatedBy = PostcodeValidator.class)
#Target( { ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface Postcode {
String message() default "not a well-formed postcode";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and I wanted to use it in combination with others like so:
#NotEmpty
#Postcode
private final String postcode;
But when attempting to test this I get the following:
java.lang.IllegalStateException: Duplicate key not a well-formed postcode
I was expecting it to fail the #NotEmpty constraint.
I know I can use composition to include one constraint within the other, but my question is, is it possible to use them as I am? And if so, what am I doing wrong?
Cheers,

How do I display the field name description in the constraint violation message of a custom constraint annotation?

How do I display the field name description in the constraint violation message of a Bean Validation 1.1 (JSR-349) custom constraint annotation?
For example, given the following custom constraint annotation #Required, resource bundle ValidationMessages.properties, and class Person, how can I compose the constraint violation message "First Name is required." for required field firstName and "Last Name is required." for required field lastName?
#Documented
#Constraint(validatedBy = {})
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
#Retention(RetentionPolicy.RUNTIME)
#NotEmpty
#ReportAsSingleViolation
public #interface Required {
public abstract Class<?>[] groups() default {};
public abstract String message() default "{Required.message}";
public abstract Class<? extends Payload>[] payload() default {};
}
In resource bundle, ValidationMessages.properties:
Required.message=is required.
Class Person:
public class Person {
#Required
private String firstName;
#Required
private String lastName;
}
ConstraintViolation constraintViolation ..
((PathImpl)constraintViolation.getPropertyPath()).getLeafNode().getName();
There is no API you can use to get the current property name. And if there were, you still would do some string manipulation to get from the property name 'firstName' to the display name "First Name".
That said, I can see the benefit of exposing the current Path in ConstraintValidatorContext which is passed to ConstraintValidator#isValid. This is per specification not possible atm, but it could be implemented as a provider specific feature. You could create a issue request for Hibernate Validator here.
Regarding your problem, the best solution imo is to add a 'labelattribute to#Required`:
public class Person {
#Required(label="First Name"
private String firstName;
#Required(label="Last Name"
private String lastName;
}
Then you can interpolate the label in the message bundle like so:
Required.message={label} is required.
The constraint would look something like this
#Documented
#Constraint(validatedBy = {})
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
#Retention(RetentionPolicy.RUNTIME)
#NotEmpty
#ReportAsSingleViolation
public #interface Required {
public abstract String label();
public abstract Class<?>[] groups() default {};
public abstract String message() default "{Required.message}";
public abstract Class<? extends Payload>[] payload() default {};
}
Note, you can add parameters you like (provided the parameter type is supported by Java).

Categories

Resources