13

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.

Tiny
  • 27,221
  • 105
  • 339
  • 599
  • If you want I can implement a DoubleRangeValidator in my collection of constraints here: http://waxolunist.github.com/validationconstraints/ - Just open an issue, you can have it tomorrow on maven central. – Christian Mar 20 '13 at 15:03
  • @ Waxolunist - Thanks but I didn't get. – Tiny Mar 21 '13 at 11:36

7 Answers7

11

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?

Hardy
  • 18,659
  • 3
  • 49
  • 65
  • 1
    I have switched to a `BigDecimal` type but the `value` attribute of `@Max` and `@Min` only accepts a `Long` value. I need to check, if a value is between a defined range. For example, starting from 0.1 to 0.9. Is this not possible with bean validation? – Tiny Mar 23 '13 at 10:53
  • 4
    For `BigDecimal` see `@DecimalMin` annotation. – Radu Dumbrăveanu Sep 19 '15 at 11:48
8

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

Sebastian
  • 877
  • 1
  • 9
  • 20
5

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 {}
}
Tiny
  • 27,221
  • 105
  • 339
  • 599
4

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

@Digits(integer = 10 /*precision*/, fraction = 2 /*scale*/)
FearlessHyena
  • 3,527
  • 33
  • 39
3

Sometimes it's convenient in pair with @AssertTrue / @AssertFalse from javax.validation.constraints

public final class WeightBean {
    @NotNull
    private Double txtWeight;  //Getter and setter.

    @AssertTrue
    public boolean getTxtWeightCheck() {
        return txtWeight > 0.1 && txtWeight < 0.9;
    }
}
Leonid Muzyka
  • 101
  • 1
  • 4
2

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

See class for validation

Flame239
  • 1,274
  • 1
  • 8
  • 20
0

Hibernate validator will work for @DecimalMin/Max on both Double & Float. But they are not exact numbers, just approximations, depending on the validation implementation, there could be some false results.

The best idea for validation is to use the BigDecimal type with the @DecimalMin and @DecimalMax validation annotations.

AConsumer
  • 2,461
  • 2
  • 25
  • 33