apply it like below:
@ValidateDateRange(start="startDate", end="endDate") public class MyEntity { private Date startDate; private Date endDate; ... }Well, this is good because it is likely that I can reuse this constraint in other entities needing validation on date ranges (I just realized in writing this that it also interesting to post a blog on an implementation of the @ValidateDateRange annotation above). The following are some of the annotations which are reusable to other entities.
- @NotNull
- @NotEmpty
- @Size
- @Min
- @Max
- @DecimalMin
- @DecimalMax
- @Pattern
The Bean Validation reference implementation has @AssertTrue annotation which can be applied to a property or getter method, but an exception is raised if you will try to access other properties value inside a getter method.
So how then could we easily validate a business logic constraint that spans multiple properties without too much work? Our answer is the reusable @AssertMethodAsTrue class-level constraint annotation that accepts a method name as a parameter. The method name that you set as parameter should return a boolean value. An example would be:
@AssertMethodAsTrue(value="isPassengerCountValid", message="Invalid passenger count!") public class Car { @Min(2) private int seatCount; @NotNull ListThe definition of the @AssertMethodAsTrue annotation are as follows:passengers; public boolean isPassengerCountValid(){ if(this.seatCount >= passengers.size()){ return true; } return false; } }
package blogspot.soadev.validator; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.ConstraintPayload; @Target( { TYPE, ANNOTATION_TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = {AssertMethodAsTrueValidator.class} ) @Documented public @interface AssertMethodAsTrue { String message() default "{value} returned false"; String value() default "isValid"; Class[] groups() default {}; Class[] payload() default {}; }
And the corresponding implementing validator class that use reflection:
package blogspot.soadev.validator; import java.lang.reflect.Method; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class AssertMethodAsTrueValidator implements ConstraintValidatorSince it is also most likely that you will have more than one method to validate, then an @AssertMethodAsTrueList would also be needed. The code are as follows:{ private String methodName; public void initialize(AssertMethodAsTrue assertMethodAsTrue) { methodName = assertMethodAsTrue.value(); } 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 (Throwable e) { System.err.println(e); } return false; } }
package blogspot.soadev.validator; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Target(value={TYPE, ANNOTATION_TYPE}) @Retention(value=RUNTIME) @Documented public @interface AssertMethodAsTrueList { AssertMethodAsTrue[] value() default {}; }With the @AssertMethodAsTrueList above you can have multiple @AssertMethodAsTrue on a single entity:
@AssertMethodAsTrueList({ @AssertMethodAsTrue(value="isPassengerCountValid", message="Invalid passenger count!"), @AssertMethodAsTrue(value="isTirePressureIdeal") }) public class Car { ... public void isPassengerCountValid(){...} public void isTirePressureIdeal(){...} }
Related Posts
- JSR 303 Bean Validation: Error "java.lang.NoSuchMethodError: javax.persistence.Persistence.getPersistenceUtil()Ljavax/persistence/PersistenceUtil;"
- JSR 303 Bean Validation: @ValidateDateRange - A reusable constraint annotation to validate date ranges
- Integrating JSR 303 Bean Validation with Oracle ADF 11g
This comment has been removed by the author.
ReplyDeleteHi Pino,
ReplyDeletethanks for the informative post. I recently wrote on a similar approach in my blog, too:
http://musingsofaprogrammingaddict.blogspot.com/2010/02/generic-class-level-constraint-for-bean.html
If you like, check it out - I'd be interested in your feedback.
Gunnar
Thanks for the feedback Gunnar. I will sure look in to your post. FYI- I was also able came by your blog for sometime before.
ReplyDeleteregards,
Pino
Couldn't you just use the AssertTrue annotation on a method like:
ReplyDelete@AssertTrue(message="Error Message")
private boolean isValid(){
...validation logic...
}
Or are you trying to make it so error messages are more specific to each individual situation?
Hi Dan,
ReplyDeleteYou cannot use the @AssertTrue on a validation that span multiple properties, like for example if you are checking if property1 is greater than property2 inside your method, then you will receive exception.
Pino
Actually you can, why would you receive an exception?
ReplyDelete