Search code examples
javahibernateannotationsvalidation

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


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!


Solution

  • 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.
    }