7

In my Spring Application, i'm Using Hibernate Validator for Validation Purpose.

When i'm doing simple Validation like @NotEmpty, @Email .. i'm easily working

But When coming to Date field, giving Problem...

Problem is in my Jsp page i'm getting values type as String then i'm convert String to Date..

Hear is my Example...

@NotEmpty(message = "Write Some Description")
private String description;

private String fromDateMonth;

private String fromDateYear;

private String toDateMonth;

private String toDateYear;

i'm converting this fromDateMonth and fromDateYear into Date in this my Controller class.

So is their any Possibility to add Validator Annotation in Controller class?

Other wise what should i'm do hear? give me suggestions...

4 Answers 4

14

If you want to validate that fromDate is before toDate, you can write a custom validator to perform this multi-field validation. You write a custom validator, and you also define a custom annotation which is placed on the bean being validated.

See the following answers for more information.

Sign up to request clarification or add additional context in comments.

Comments

5

Based on the number of times the question is displayed, I conclude that a lot of people still end up here looking for a validator for a date range.

Suppose we have a DTO containing another DTO (or class with @Embeddable annotation) with two fields with LocalDate (or other date representation, it's only example).

class MyDto {

    @ValidDateRange // <-- target annotation
    private DateRange dateRange;
    
    // getters, setters, etc.
}
class DateRange {

    private LocalDate startOfRange;
    private LocalDate endOfRange;

    // getters, setters, etc.
}

Now, we can declare our own @ValidDateRange annotation:

@Documented
@Constraint(validatedBy = DateRangeValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface ValidDateRange {

    String message() default "Start date must be before end date";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };
}

Writing the validator itself also comes down to a few lines of code:

class DateRangeValidator implements ConstraintValidator<ValidDateRange, DateRange> {
    @Override
    public boolean isValid(DateRange value, ConstraintValidatorContext context) {
        return value.getStartOfRange().isBefore(value.getEndOfRange());
    }
}

1 Comment

class DateRangeValidator should be public. Moreover it is very useful to define an annotation for only one class. In my opinion the exemple can be improved by changing class DateRange into an interface.
3

Reference: https://blog.tericcabrel.com/write-custom-validator-for-body-request-in-spring-boot/

Create DateRange annotation

@Constraint(validatedBy = DateRangeValidator.class)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented

public @interface DateRange {
    String message() default "{mob.concept.admin.models.constraint.DateRange.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    String before();

    String after();
}

create validator Constraint

public class DateRangeValidator implements ConstraintValidator<DateRange, Object> {
    private String beforeFieldName;
    private String afterFieldName;

    @Override
    public void initialize(DateRange constraintAnnotation) {
        beforeFieldName = constraintAnnotation.before();
        afterFieldName = constraintAnnotation.after();
    }

    @Override
    public boolean isValid(final Object value, ConstraintValidatorContext context) {
        try {
            final Field beforeDateField = value.getClass().getDeclaredField(beforeFieldName);
            beforeDateField.setAccessible(true);

            final Field afterDateField = value.getClass().getDeclaredField(afterFieldName);
            afterDateField.setAccessible(true);

            final LocalDate beforeDate = (LocalDate) beforeDateField.get(value);
            final LocalDate afterDate = (LocalDate) afterDateField.get(value);
            return beforeDate.isEqual(afterDate) || beforeDate.isBefore(afterDate);

        } catch (NoSuchFieldException | IllegalAccessException ignored) {
            return false;
        }
    }
}

Example

@DateRange(before = "startDate", after = "endDate", message = "Start date should be less than end date")
public class OfferDto {
    @NotNull(message = "offer title required")
    @Size(max = 20, min = 8, message = "Offer title must be between 8 and 20 characters")
    private String offerTitle;
    @Size(max = 100, min = 8, message = "Offer title must be between 8 and 100 characters")
    @NotNull(message = "offer description required")
    private String description;
    @NotNull(message = "start date is required")
    private LocalDate startDate;
    @NotNull(message = "end date is required")
    private LocalDate endDate;
    @NotNull(message = "discount is required")
    private Float discount;
    @NotNull(message = "offer type is required")
    @EnumValidation(enumsClass = OfferType.class)
    private String offerType;
}

Comments

1

For sure you can use Bean Validation inside your controller, just add an annotation on the property like you did in your model. But the best way would be use a Date type in your model instead of string.

3 Comments

if type is Date ... So simple but Problem is my jsp has two select box es and one is for month and one is for Year .. so that's way i'm taking Strings...
In my controller class, first compare Two Dates, if fromdate is greater than to todate .. i'm add error to BindingResult object but how to add and in Bean class which annotation will add?
It might be more simple if you use a Date object in your model, then use a date field in your JSF, you'll be able to use the annotations directly inside your model and test if the date is before or after today (if I understood well your last comment) with the @Past or @Future annotations

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.