BeanValidationForm and JSR-303

I use BeanValidationForm with annotated constrains. It works fine for standard annotations (@NotNull, @Min, @Size …)
I have defined my own class level jsr-303 annotation which gets piblic method of class:

@AssertMethodAsTrueList(value = {
        @AssertMethodAsTrue(value = "isUnique", message = "This record is not unique!")
})

This works fine without vaadin (I have tested it with spring framework and spring roo).

    @Transactional
    public boolean doCommit() {
        try {
            getForm().commit();
            E entity = getEntityForItem(getForm().getItemDataSource());
            if (!getTableContainer().containsId(getIdForEntity(entity))) {
                ((JPAContainer<E>) getTableContainer()).addEntity(entity);
            }
            return true;
        } catch (Validator.InvalidValueException e) {
            // show validation error also on the save button
            getForm().setCommitErrorMessage(e.getMessage());
            return false;
        } catch (Exception e) {
            // show validation error also on the save button
            getForm().setCommitErrorMessage(e.getMessage());
            return false;
        }
    }

Standard annotations throw exceptions Validator.InvalidValueException on Validator.InvalidValueException (correct behavoir)
My annotation throws: [quote]
javax.validation.ConstraintViolationException: Validation failed for classes [Country]
during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[ConstraintViolationImpl{interpolatedMessage=‘This record is not unique!’, propertyPath=, rootBeanClass=class Country, messageTemplate=‘This record is not unique!’}]

[/quote] on ((JPAContainer) getTableContainer()).addEntity(entity);


How can i correctly handle that?

How is the isUnique() method implemented? Is it possible to execute it at any time? Does it do database access? On what kind of a field do you have it (id/other, type)? Are the related fields immediate?

It looks like BeanValidationValidator is not seeing the constraint violation at all, but it is only caught by your JPA provider (Hibernate or EclipseLink?) when saving the entity - it is using the same annotations. Maybe there is no constraint violation before saving the entity, or the method does not check for such outside JPA methods - is there a real constraint violation before saving the entity.

isUnique do database access (select) and looks for rows with same field Code value within time validity (validFrom, validTill).

    public boolean isUnique(Object object, String code) {
        return isSuspended() || (entityManager.createQuery("select count(o) from " + object.getClass().getName() + " o where " +
                "o.id <> :id and o.code = :code and o.suspended = false and (" +
                "o.validFrom between :validFrom and :validTill or " +
                "o.validTill between :validFrom and :validTill or " +
                ":validFrom between o.validFrom and o.validTill )", Long.class)
                  .setFlushMode(FlushModeType.COMMIT)
                  .setParameter("id", getId() == null ? -1 : getId())
                  .setParameter("code", code)
                  .setParameter("validFrom", getValidFrom())
                  .setParameter("validTill", getValidTill())
                  .getSingleResult() == 0);
    }

@Documented
@Constraint(validatedBy = {AssertMethodAsTrueValidator.class})
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AssertMethodAsTrue {
    String message() default "{value} returned false";

    String value() default "isValid";

    Class[] groups() default {};

    Class[] payload() default {};
}

public class AssertMethodAsTrueValidator implements ConstraintValidator<AssertMethodAsTrue, Object> {
    private String methodName;

    @Override
    public void initialize(AssertMethodAsTrue assertMethodAsTrue) {
        methodName = assertMethodAsTrue.value();
    }

    @Override
    public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {
        try {
            Class clazz = object.getClass();
            Method validate = clazz.getMethod(methodName, new Class[0]
);
            return (Boolean) validate.invoke(object);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return false;
    }
}

Sorry to resurrect this, but from what I can see in current BeanFieldGroup/BeanValidator implementation (version 7.1.8), Vaadin simply doesn’t take class-level validators into account, it only uses property/method validator annotations. I understand the logic behind this (even though I don’t agree with it), but the important thing is that it’s not mentioned anywhere, as far as I can see. Please consider adding this piece of information to Vaadin documentation.