2

How to customize the validation annotation based on some criteria using javax validation with Hibernate validation implementation.

Sample code:

import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;

import org.springframework.stereotype.Component;

import com.kp.mechanic.workshop.validator.beans.GeoLocation;
import com.kp.mechanic.workshop.validator.beans.Practioner;
import com.kp.mechanic.workshop.validator.beans.PractionerAddress;

@Component("validator")
public class ValidatorService {

    private  Validator validator;
    private  ValidatorFactory factory;

    public void execute() {
        System.out.println(" Validation framework starts");
        try {

            //  < DAO call to get GEO Location is AUS >

            //Construct Geo Location:
            GeoLocation geoLocation= new GeoLocation();
            geoLocation.setStrtAddrLine1("walker street ");
            geoLocation.setOptionalAddrLine2("bonitoa road");
            geoLocation.setZipCD("SY");


             factory = Validation.buildDefaultValidatorFactory();
             validator = factory.getValidator();
             Set<ConstraintViolation<Object>> resultSet= validator.validate(geoLocation);

             for (ConstraintViolation<Object> object : resultSet) {
                   System.out.println(object.getPropertyPath() + ": " + object.getMessage());
               }


        }
        catch(Exception e) {
            System.out.println("Message "+e);
            e.printStackTrace();
        }

    }
}

Refer the given below POJO using Lombok.

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class GeoLocation {
//City name
        @NotNull(message="warn:Missing data")
        @Size(min =3 , max =50, message = "warn:Invalid length")
        @Pattern(regexp = "^a-zA-Z",message = "warn:Invalid characters found in text",flags=Pattern.Flag.CASE_INSENSITIVE)
        private String cityNM;

        //State Code
        @NotNull(message="warn:Missing data")
        @Size(min =2 , max =2, message = "warn:Invalid Length")
        @Pattern(regexp = "^[a-zA-Z]",message = "warn:Invalid characters found in text",flags=Pattern.Flag.CASE_INSENSITIVE)
        private String stateCD;

        //zip code
        @NotNull(message="warn:Missing data")
        @Size(min =5 , max =9, message = "warn:Invalid Length")
        @Pattern(regexp = "^[0-9]",message = "warn:Invalid characters found in text")
        private String zipCD;
}  

Using given above pom entries .

The above code is working fine for the given below validation rules for GEO Location is "AUS".

> CityName   : not null , minimum 3 and maximum 50 characters, only alphabets.
> State Code : not null , maximum 2 , only alphabets. 
> Zip  Code  : not null , minimum 5 and maximum 9, only digits.

Where as for "IND" , i would like to change the given below validation rules as such.

> CityName   : not null , minimum 10 and maximum 15 characters, only alphabets
> State Code : not null , maximum 6, only alphabets. 
> Zip  Code  : not null , maximum 10, only digits

Can you give any suggestion to change the validation rules based on the geo location type is IND?

This is a kind of Custom Annotation, is there any better approach to reuse the annotation without writing java logic in custom annotation class ? Custom Annotation for cross fields

Vasanth
  • 474
  • 2
  • 9
  • 31

2 Answers2

0

First of all appreciate for a nice question.

In above scenario I would go for custom annotation based validator as shown below.

package com.example.demo;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.FIELD;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Constraint(validatedBy = StateCodeValidator.class)
@Documented
@Target({METHOD, FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface StateValidate {   
    String message() default "Invalid State";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}


package com.example.demo;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.springframework.beans.factory.annotation.Autowired;

public class StateValidator implements ConstraintValidator<StateValidate, String>{

    @Autowired
    private HttpServletRequest httpServletRequest;

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        //TODO you can check code size as per requirement
        }        
    }
}

You need to annotate @StateValidate at any field you looking for

This will be more controlled approach and if needed i18 can be used to manage to support multiple countries.

Swarit Agarwal
  • 2,520
  • 1
  • 26
  • 33
0

I got reference from given below links.
stackover flow .
Programmatic constraint definition

 HibernateValidatorConfiguration configuration = Validation.byProvider(HibernateValidator.class).configure();   
             ConstraintMapping constraintMapping = configuration.createConstraintMapping();
 if(geoLocation.getType().equals("AUS")) {
                 constraintMapping
                 .type(GeoLocation.class)
                 .property("cityName", ElementType.FIELD)
                 .constraint(new NotNullDef().message("cityName should not empty")) 
                 .constraint(new SizeDef().min(3).message("Minimum 3 charater"))
                 .constraint(new SizeDef().max(50).message("Maximum 50 character"));
                 }else if(geoLocation.getType().equals("IND")) {
                     constraintMapping
                     .type(GeoLocation.class)
                     .property("cityName", ElementType.FIELD)
                     .constraint(new NotNullDef().message("cityName should not empty"))                
                     .constraint(new SizeDef().min(10).message("Minimum 10 charater"))
                     .constraint(new SizeDef().max(15).message("Maximum 15 character"));

                 }


             validator=configuration.addMapping(constraintMapping).buildValidatorFactory().getValidator();
             resultSet= validator.validate(geoLocation);
             for (ConstraintViolation<Object> object : resultSet) {
               System.out.println(object.getPropertyPath() + ": " + object.getMessage());
           }
Vasanth
  • 474
  • 2
  • 9
  • 31