Hibernate validation annotation - validate that at least one field is not null

10,412

Solution 1

I finally wrote the whole validator:

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

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

import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;

import org.apache.commons.beanutils.PropertyUtils; 

@Target( { TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = CheckAtLeastOneNotNull.CheckAtLeastOneNotNullValidator.class)
@Documented
public @interface CheckAtLeastOneNotNull {
    
     String message() default "{com.xxx.constraints.checkatleastnotnull}";

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

     Class<? extends Payload>[] payload() default {};
        
     String[] fieldNames();
        
     public static class CheckAtLeastOneNotNullValidator implements ConstraintValidator<CheckAtLeastOneNotNull, Object> {
            
         private String[] fieldNames;

         public void initialize(CheckAtLeastOneNotNull constraintAnnotation) {
             this.fieldNames = constraintAnnotation.fieldNames();
         }

         public boolean isValid(Object object, ConstraintValidatorContext constraintContext) {

             if (object == null) {
                 return true;
             }
             try { 
                 for (String fieldName:fieldNames){
                     Object property = PropertyUtils.getProperty(object, fieldName);
                        
                     if (property != null) return true;
                 }
                 return false;
             } catch (Exception e) {
                 return false;
             }
         }
     }
}

Example of usage:

@Entity
@CheckAtLeastOneNotNull(fieldNames={"fieldA","fieldB"})
public class Reward {

    @Id
    @GeneratedValue
    private Integer id;

    private Integer fieldA;
    private Integer fieldB;

    [...] // accessors, other fields, etc.
}

Solution 2

Just write your own validator. Is't should be pretty simple: iterate over field names and get field values by using reflection.

Concept:

Collection<String> values = Arrays.asList(
    BeanUtils.getProperty(obj, fieldA),
    BeanUtils.getProperty(obj, fieldB),
);

return CollectionUtils.exists(values, PredicateUtils.notNullPredicate());

There I used methods from commons-beanutils and commons-collections.

Share:
10,412

Related videos on Youtube

Resh32
Author by

Resh32

Updated on September 16, 2022

Comments

  • Resh32
    Resh32 almost 2 years

    Is there a way to define a Hibernate validation rule using annotations as defined here, stating that at least one field shall be not null?

    This would be a hypothetical example (@OneFieldMustBeNotNullConstraint does not really exist):

    @Entity
    @OneFieldMustBeNotNullConstraint(list={fieldA,fieldB})
    public class Card {
    
        @Id
        @GeneratedValue
        private Integer card_id;
    
        @Column(nullable = true)
        private Long fieldA;
    
        @Column(nullable = true)
        private Long fieldB;
    
    }
    

    In the illustrated case, fieldA can be null or fieldB can be null, but not both.

    One way would be to create my own validator, but I'd like to avoid if it already exists. Please share one validator if you have one already made... thanks!

  • Resh32
    Resh32 almost 12 years
    Thanks, that helped me write the introspection part using PropertyUtils.getProperty.