Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validating double and float values using Hibernate Validator - bean validation

I'm looking for a way to validate a java.lang.Double field in the Spring command bean for its maximum and minimum values (a value must lie between a given range of values) like,

public final class WeightBean
{
     @Max(groups={ValidationGroup.class}, value=Double.MAX_VALUE, message="some key or default message")
     @Min(groups={ValidationGroup.class}, value=1D, message="some key or default message")
     private Double txtWeight;  //Getter and setter.

     public interface ValidationGroup{}         
}

But both @Max and @Min cannot take a java.lang.Double value.

Note that double and float are not supported due to rounding errors (some providers might provide some approximative support)

So what is the way of validating such fields?

I'm working with Spring 3.2.0 and Hibernate Validator 4.3.1 CR1.

like image 438
Tiny Avatar asked Mar 18 '13 23:03

Tiny


5 Answers

Newer versions of Hibernate Validator (at least 6.0.17) supports @DecimalMin/Max annotation on double

See class for validation

like image 200
Flame239 Avatar answered Sep 19 '22 01:09

Flame239


You can also use @Digits from the hibernate validator API as well

@Digits(integer = 10 /*precision*/, fraction = 2 /*scale*/)
like image 33
FearlessHyena Avatar answered Sep 17 '22 01:09

FearlessHyena


You can use the annotation, but you might get false results depending. This is a general problem with doubles and imo in many cases _Double_s should be avoided. Maybe switching to a different type is the best solution? BigDecimal for example?

like image 11
Hardy Avatar answered Oct 17 '22 22:10

Hardy


If you have switched to BigDecimal (or BigInteger), you could use @DecimalMin or @DecimalMax. But this is still no solution for float or double.

like image 8
Sebastian Avatar answered Oct 17 '22 20:10

Sebastian


I have avoided the double and the float types and implemented a custom validator that could validate a BigDecimal value based on the precision and the scale.

The constraint descriptor.

package constraintdescriptor;

import constraintvalidator.BigDecimalRangeValidator;
import java.lang.annotation.Documented;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;

@Target({METHOD, FIELD, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = BigDecimalRangeValidator.class)
@Documented
public @interface BigDecimalRange {
    public String message() default "{java.math.BigDecimal.range.error}";
    public Class<?>[] groups() default {};
    public Class<? extends Payload>[] payload() default {};

    long minPrecision() default Long.MIN_VALUE;
    long maxPrecision() default Long.MAX_VALUE;
    int scale() default 0;
}

The constraint validator.

package constraintvalidator;

import constraintdescriptor.BigDecimalRange;
import java.math.BigDecimal;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public final class BigDecimalRangeValidator implements ConstraintValidator<BigDecimalRange, Object> {

    private long maxPrecision;
    private long minPrecision;
    private int scale;

    @Override
    public void initialize(final BigDecimalRange bigDecimalRange) {
        maxPrecision = bigDecimalRange.maxPrecision();
        minPrecision = bigDecimalRange.minPrecision();
        scale = bigDecimalRange.scale();
    }

    @Override
    public boolean isValid(final Object object, final ConstraintValidatorContext cvc) {
        boolean isValid = false;

        if (object == null) { // This should be validated by the not null validator (@NotNull).
            isValid = true;
        } else if (object instanceof BigDecimal) {
            BigDecimal bigDecimal = new BigDecimal(object.toString());
            int actualPrecision = bigDecimal.precision();
            int actualScale = bigDecimal.scale();
            isValid = actualPrecision >= minPrecision && actualPrecision <= maxPrecision && actualScale <= scale;

            if (!isValid) {
                cvc.disableDefaultConstraintViolation();
                cvc.buildConstraintViolationWithTemplate("Precision expected (minimun : " + minPrecision + ", maximum : " + maxPrecision + "). Maximum scale expected : " + scale + ". Found precision : " + actualPrecision + ", scale : " + actualScale).addConstraintViolation();
            }
        }

        return isValid;
    }
}

This could be extended for other types as well, as and when required.


And finally in the bean, the property of the type BigDecimal could be annotated by the @BigDecimalRange annotation as follows.

package validatorbeans;

public final class WeightBean {

    @BigDecimalRange(minPrecision = 1, maxPrecision = 33, scale = 2, groups = {ValidationGroup.class}, message = "The precision and the scale should be less than or equal to 35 and 2 respectively.")
    private BigDecimal txtWeight; // Getter and setter.

    public interface ValidationGroup {}
}
like image 5
Tiny Avatar answered Oct 17 '22 22:10

Tiny