Dariush Jafari
Dariush Jafari

Reputation: 5443

spring combine two validation annotations in one

I'm using Spring+Hibernate+Spring-MVC.
I want to define a custom constraint combining two other predefined validation annotations: @NotNull @Size like this:

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@NotNull
@Size(min=4)
public @interface JPasswordConstraint {
} // this is not correct. It's just a suggestion.

and I want to use this annotation in my form models.

public class ChangePasswordForm {

  @NotNull
  private String currentPass;

  @JPasswordConstraint
  private String newPass;

  @JPasswordConstraint
  private String newPassConfirm;
}

UserController.java

@RequestMapping(value = "/pass", method = RequestMethod.POST)
public String pass2(Model model, @Valid @ModelAttribute("changePasswordForm") ChangePasswordForm form, BindingResult result) {
    model.addAttribute("changePasswordForm", form);
    try {
        userService.changePassword(form);
    } catch (Exception ex) {
        result.rejectValue(null, "error.objec", ex.getMessage());
        System.out.println(result);
    }
    if (!result.hasErrors()) {
        model.addAttribute("successMessage", "password changed successfully!");
    }
    return "user/pass";
}

But it does not work. It accepts the less than 4 character passwords.
How can I solve this problem?

Upvotes: 18

Views: 16199

Answers (2)

FBH
FBH

Reputation: 793

You can combine several validation annotations into one the following way :

(This is an exemple which combines @Pattern(regexp = "[a-z]") AND @Size(min = 2, max = 3) )

    @Pattern(regexp = "[a-z]")
    @Size(min = 2, max = 3)
    @ReportAsSingleViolation
    @Target({ METHOD, FIELD })
    @Retention(RUNTIME)
    @Constraint(validatedBy = { })
    public @interface PatternOrSize {
        String message() default "";

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

Note that by default the annotations conditions follow the AND logic (i.e. all of them must be true).

For special needs, you can use the OR logic. You do so by using @ConstraintComposition(OR)

For example if you want @NotNull OR `@Size`` do the following :

    @ConstraintComposition(OR) // <---
    @NotNull
    @Size(min = 2, max = 3)
    @ReportAsSingleViolation
    @Target({ METHOD, FIELD })
    @Retention(RUNTIME)
    @Constraint(validatedBy = { })
    public @interface PatternOrSize {
        String message() default "";

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

Upvotes: 7

mavarazy
mavarazy

Reputation: 7735

This is a bit late, but technique of combining validation annotations described in

https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/?v=5.4#section-constraint-composition

Maybe it was not available, at the time of writing, but solution is following

@NotNull
@Size(min=4)
@Target({ METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { })
@Documented
public @interface JPasswordConstraint {
    String message() default "Password is invalid";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
 }

Upvotes: 21

Related Questions