Wednesday, August 25, 2010

JPA JSR303 Spring Form Validation

In an ideal world you should declare just once your validation rules and reuse them from javascript in forms, from java when binding your form and of course when persisting to the database. Spring validators and JSR303 address the second and JPA the third. The first can be achieved with JQuery. But the question remains "How can I declare my validations just once"

JPA 2.0 supports JSR 303 annotations.

You can build your own annotations for custom validators. In addition you could use Spring Validators and a little bit of reflection:

public class MyClassValidator implements Validator {
 
    @Override
    public boolean supports(Class clazz) {
        return MyClass.class.isAssignableFrom(clazz);
    }
 
    @Override
    public void validate(Object target, Errors errors) {
         
        Field[] fields = target.getClass().getDeclaredFields();
        for(Field field : fields) {
            boolean accessible = field.isAccessible();
            try {
                if(! accessible) {
                    field.setAccessible(true);
                }
                Annotation[] annotations = field.getAnnotations();
                for(Annotation annotation : annotations) {
                    //Avoiding cyclic references for @CustomTransient 
                    //@Column(nullable = false, length = 50)
                    if( annotation instanceof Column) {
                        Column column = (Column) annotation;
                         
                        if( String.class == field.getType() ){
                            String fieldValue = (String) field.get(target);
                            if( !column.nullable() && Utils.isEmpty(fieldValue) ) {
                                rejectValue(errors, field.getName(), "cannotBeNull");
                            } else if(fieldValue.length() > column.length()){
                                rejectValue(errors, field.getName(), "lengthMustBeLessThan", new Object[] {column.length()});
                            }
                        }
                    }
                }
            } catch (SecurityException e) {
                //Not interested in these cases
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                if(! accessible) {
                    field.setAccessible(false);
                }
            }
        }
    }
     
    private void rejectValue(Errors errors, String fieldName, String code) {
        rejectValue(errors, fieldName, code, null);
    }
  
    private void rejectValue(Errors errors, String fieldName, String code, Object[] params) {
        errors.rejectValue(fieldName, code, params, "?" + code + "?");
    }
}
Annotations then can be used from a taglib to provide in the front end the basic validations already available in the backend. That is perhaps the subject for another post.

No comments:

Followers