Comments 0

Document transcript

Hibernate ValidatorReference GuideVersion:3.0.0.GATable of ContentsPreface............................................................................................................................................iii1.Defining constraints....................................................................................................................11.1.What is a constraint?..........................................................................................................11.2.Built in constraints.............................................................................................................11.3.Error messages...................................................................................................................31.4.Writing your own constraints..............................................................................................31.5.Annotating your domain model...........................................................................................42.Using the Validator framework...................................................................................................72.1.Database schema-level validation........................................................................................72.2.ORMintegration................................................................................................................72.2.1.Hibernate event-based validation..............................................................................72.2.2.Java Persistence event-based validation....................................................................82.3.Application-level validation................................................................................................82.4.Presentation layer validation...............................................................................................92.5.Validation informations......................................................................................................9Hibernate 3.0.0.GA iiPrefaceAnnotations are a very convenient and elegant way to specify invariant constraints for a domain model.Youcan,for example,express that a property should never be null,that the account balance should be strictly posit-ive,etc.These domain model constraints are declared in the bean itself by annotating its properties.A validatorcan then read them and check for constraint violations.The validation mechanism can be executed in differentlayers in your application without having to duplicate any of these rules (presentation layer,data access layer).Following the DRY principle,Hibernate Validator has been designed for that purpose.Hibernate Validator works at two levels.First,it is able to check in-memory instances of a class for constraintviolations.Second,it can apply the constraints to the Hibernate metamodel and incorporate theminto the gener-ated database schema.Each constraint annotation is associated to a validator implementation responsible for checking the constrainton the entity instance.A validator can also (optionally) apply the constraint to the Hibernate metamodel,allow-ing Hibernate to generate DDL that expresses the constraint.With the appropriate event listener,you can ex-ecute the checking operation on inserts and updates done by Hibernate.Hibernate Validator is not limited to usewith Hibernate.You can easily use it anywhere in your application as well as with any Java Persistence pro-vider (entity listener provided).When checking instances at runtime,Hibernate Validator returns information about constraint violations in anarray of InvalidValue s.Among other information,the InvalidValue contains an error description messagethat can embed the parameter values bundle with the annotation (eg.length limit),and message strings that maybe externalized to a ResourceBundle.Hibernate 3.0.0.GA iiiChapter 1.Defining constraints1.1.What is a constraint?A constraint is a rule that a given element (field,property or bean) has to comply to.The rule semantic is ex-pressed by an annotation.A constraint usually has some attributes used to parameterize the constraints limits.The constraint applies to the annotated element.1.2.Built in constraintsHibernate Validator comes with some built-in constraints,which covers most basic data checks.As we'll seelater,you're not limited to them,you can literally in a minute write your own constraints.Table 1.1.Built-in constraintsAnnotationApply onRuntime checkingHibernate Metadata im-pact@Length(min=,max=)property (String)check if the string lengthmatch the rangeColumn length will be setto max@Max(value=)property (numeric orstring representation of anumeric)check if the value is lessthan or equals to maxAdd a check constraint onthe column@Min(value=)property (numeric orstring representation of anumeric)check if the value is morethan or equals to minAdd a check constraint onthe column@NotNullpropertycheck if the value is notnullColumn(s) are not null@NotEmptypropertycheck if the string is notnull nor empty.Check ifthe connection is not nullnor emptyColumn(s) are not null(for String)@Pastproperty (date or calen-dar)check if the date is in thepastAdd a check constraint onthe column@Futureproperty (date or calen-dar)check if the date is in thefuturenone@Pattern(regex="regexp",flag=) or @Patterns({@Pattern(...)} )property (string)check if the propertymatch the regular expres-sion given a match flag(seejava.util.regex.Pattern )none@Range(min=,max=)property (numeric orstring representation of anumeric)check if the value isbetween min and max(included)Add a check constraint onthe columnHibernate 3.0.0.GA 1AnnotationApply onRuntime checkingHibernate Metadata im-pact@Size(min=,max=)property (array,collec-tion,map)check if the element sizeis between min and max(included)none@AssertFalsepropertycheck that the methodevaluates to false (usefulfor constraints expressedin code rather than an-notations)none@AssertTruepropertycheck that the methodevaluates to true (usefulfor constraints expressedin code rather than an-notations)none@Validproperty (object)perform validation re-cursively on the associ-ated object.If the objectis a Collection or an ar-ray,the elements are val-idated recursively.If theobject is a Map,the valueelements are validated re-cursively.none@Emailproperty (String)check whether the stringis conform to the emailaddress specificationnone@CreditCardNumberproperty (String)check whether the stringis a well formated creditcard number (derivativeof the Luhn algorithm)none@Digitsproperty (numeric orstring representation of anumeric)check whether the prop-erty is a number havingup to integerDigits in-teger digits and frac-tionalDigits fractonaldigitsdefine column precisionand scale@EANproperty (string)check whether the stringis a properly formatedEAN or UPC-A codenone@Digitsproperty (numeric orstring representation of anumeric)check whether the prop-erty is a number havingup to integerDigits in-teger digits and frac-tionalDigits fractonaldigitsdefine column precisionand scaleDefining constraintsHibernate 3.0.0.GA 21.3.Error messagesHibernate Validator comes with a default set of error messages translated in about ten languages (if yours is notpart of it,please sent us a patch).You can override those messages by creating a ValidatorMes-sages.properties or ( ValidatorMessages_loc.properties ) and override the needed keys.You can evenadd your own additional set of messages while writing your validator annotations.If Hibernate Validator can-not resolve a key from your resourceBundle nor from ValidatorMessage,it falls back to the default built-in val-ues.Alternatively you can provide a ResourceBundle while checking programmatically the validation rules on abean or if you want a completly different interpolation mechanism,you can provide an implementation oforg.hibernate.validator.MessageInterpolator (check the JavaDoc for more informations).1.4.Writing your own constraintsExtending the set of built-in constraints is extremely easy.Any constraint consists of two pieces:the constraintdescriptor (the annotation) and the constraint validator (the implementation class).Here is a simple user-defined descriptor:@ValidatorClass(CapitalizedValidator.class)@Target(METHOD)@Retention(RUNTIME)@Documentedpublic @interface Capitalized {CapitalizeType type() default Capitalize.FIRST;String message() default"has incorrect capitalization"}type is a parameter describing how the property should to be capitalized.This is a user parameter fully depend-ant on the annotation business.message is the default string used to describe the constraint violation and is mandatory.You can hard code thestring or you can externalize part/all of it through the Java ResourceBundle mechanism.Parameters values aregoing to be injected inside the message when the {parameter} string is found (in our example Capitalizationis not {type} would generate Capitalization is not FIRST ),externalizing the whole string in Valid-atorMessages.properties is considered good practice.See Error messages.@ValidatorClass(CapitalizedValidator.class)@Target(METHOD)@Retention(RUNTIME)@Documentedpublic @interface Capitalized {CapitalizeType type() default Capitalize.FIRST;String message() default"{validator.capitalized}";}#in ValidatorMessages.propertiesvalidator.capitalized = Capitalization is not {type}As you can see the {} notation is recursive.To link a descriptor to its validator implementation,we use the @ValidatorClass meta-annotation.The validat-or class parameter must name a class which implements Validator<ConstraintAnnotation>.Defining constraintsHibernate 3.0.0.GA 3We now have to implement the validator (ie.the rule checking implementation).A validation implementationcan check the value of the a property (by implementing PropertyConstraint ) and/or can modify the hibernatemapping metadata to express the constraint at the database level (by implementing PersistentClassCon-straint )public class CapitalizedValidatorimplements Validator<Capitalized>,PropertyConstraint {private CapitalizeType type;//part of the Validator<Annotation> contract,//allows to get and use the annotation valuespublic void initialize(Capitalized parameters) {type = parameters.type();}//part of the property constraint contractpublic boolean isValid(Object value) {if (value==null) return true;if (!(value instanceof String) ) return false;String string = (String) value;if (type == CapitalizeType.ALL) {return string.equals( string.toUpperCase() );}else {String first = string.substring(0,1);return first.equals( first.toUpperCase();}}}The isValid() method should return false if the constraint has been violated.For more examples,refer to thebuilt-in validator implementations.We only have seen property level validation,but you can write a Bean level validation annotation.Instead ofreceiving the return instance of a property,the bean itself will be passed to the validator.To activate the valida-tion checking,just annotated the bean itself instead.A small sample can be found in the unit test suite.If your constraint can be applied multiple times (with different parameters) on the same property or type,youcan use the following annotation form:@Target(METHOD)@Retention(RUNTIME)@Documentedpublic @interface Patterns {Pattern[] value();}@Target(METHOD)@Retention(RUNTIME)@Documented@ValidatorClass(PatternValidator.class)public @interface Pattern {String regexp();}Basically an annotation containing the value attribute as an array of validator annotations.1.5.Annotating your domain modelSince you are already familiar with annotations now,the syntax should be very familiarpublic class Address {Defining constraintsHibernate 3.0.0.GA 4private String line1;private String line2;private String zip;private String state;private String country;private long id;//a not null string of 20 characters maximum@Length(max=20)@NotNullpublic String getCountry() {return country;}//a non null string@NotNullpublic String getLine1() {return line1;}//no constraintpublic String getLine2() {return line2;}//a not null string of 3 characters maximum@Length(max=3) @NotNullpublic String getState() {return state;}//a not null numeric string of 5 characters maximum//if the string is longer,the message will//be searched in the resource bundle at key'long'@Length(max=5,message="{long}")@Pattern(regex="[0-9]+")@NotNullpublic String getZip() {return zip;}//should always be true@AssertTruepublic boolean isValid() {return true;}//a numeric between 1 and 2000@Id @Min(1)@Range(max=2000)public long getId() {return id;}}While the example only shows public property validation,you can also annotate fields of any kind of visibility@MyBeanConstraint(max=45public class Dog {@AssertTrue private boolean isMale;@NotNull protected String getName() {...};...}You can also annotate interfaces.Hibernate Validator will check all superclasses and interfaces extended or im-plemented by a given bean to read the appropriate validator annotations.public interface Named {Defining constraintsHibernate 3.0.0.GA 5@NotNull String getName();...}public class Dog implements Named {@AssertTrue private boolean isMale;public String getName() {...};}The name property will be checked for nullity when the Dog bean is validated.Defining constraintsHibernate 3.0.0.GA 6Chapter 2.Using the Validator frameworkHibernate Validator is intended to be used to implement multi-layered data validation,where constraints are ex-pressed in a single place (the annotated domain model) and checked in various different layers of the applica-tion.This chapter will cover Hibernate Validator usage for different layers2.1.Database schema-level validationOut of the box,Hibernate Annotations will translate the constraints you have defined for your entities into map-ping metadata.For example,if a property of your entity is annotated @NotNull,its columns will be declared asnot null in the DDL schema generated by Hibernate.Using hbm2ddl,domain model constraints will be expressed into the database schema.If,for some reason,the feature needs to be disabled,set hibernate.validator.apply_to_ddl to false.2.2.ORM integrationHibernate Validator integrates with both Hibernate and all pure Java Persistence providers2.2.1.Hibernate event-based validationHibernate Validator has two built-in Hibernate event listeners.Whenever a PreInsertEvent or PreUp-dateEvent occurs,the listeners will verify all constraints of the entity instance and throw an exception if anyconstraint is violated.Basically,objects will be checked before any inserts and before any updates made by Hi-bernate.This includes changes applied by cascade!This is the most convenient and the easiest way to activatethe validation process.On constraint violation,the event will raise a runtime InvalidStateException whichcontains an array of InvalidValues describing each failure.If Hibernate Validator is present in the classpath,Hibernate Annotations (or Hibernate EntityManager) will useit transparently.If,for some reason,you want to disable this integration,set hibern-ate.validator.autoregister_listeners to falseNoteIf the beans are not annotated with validation annotations,there is no runtime performance cost.In case you need to manually set the event listeners for Hibernate Core,use the following configuration in hi-bernate.cfg.xml:<hibernate-configuration>...<event type="pre-update"><listenerclass="org.hibernate.validator.event.ValidateEventListener"/></event><event type="pre-insert"><listenerclass="org.hibernate.validator.event.ValidateEventListener"/></event>Hibernate 3.0.0.GA 7</hibernate-configuration>2.2.2.Java Persistence event-based validationHibernate Validator is not tied to Hibernate for event based validation:a Java Persistence entity listener isavailable.Whenever an listened entity is persisted or updated,Hibernate Validator will verify all constraints ofthe entity instance and throw an exception if any constraint is violated.Basically,objects will be checked be-fore any inserts and before any updates made by the Java Persistence provider.This includes changes appliedby cascade!On constraint violation,the event will raise a runtime InvalidStateException which contains anarray of InvalidValues describing each failure.Here is how to make a class validatable:@Entity@EntityListeners( JPAValidateListener.class )public class Submarine {...}NoteCompared to the Hibernate event,the Java Persistence listener has two drawbacks.You need to definethe entity listener on every validatable entity.The DDL generated by your provider will not reflect theconstraints.2.3.Application-level validationHibernate Validator can be applied anywhere in your application code.ClassValidator personValidator = new ClassValidator( Person.class );ClassValidator addressValidator = new ClassValidator( Address.class,ResourceBundle.getBundle("messages",Locale.ENGLISH) );InvalidValue[] validationMessages = addressValidator.getInvalidValues(address);The first two lines prepare the Hibernate Validator for class checking.The first one relies upon the error mes-sages embedded in Hibernate Validator (see Error messages),the second one uses a resource bundle for thesemessages.It is considered a good practice to execute these lines once and cache the validator instances.The third line actually validates the Address instance and returns an array of InvalidValues.Your applicationlogic will then be able to react to the failure.You can also check a particular property instead of the whole bean.This might be useful for property per prop-erty user interactionClassValidator addressValidator = new ClassValidator( Address.class,ResourceBundle.getBundle("messages",Locale.ENGLISH) );//only get city property invalid valuesInvalidValue[] validationMessages = addressValidator.getInvalidValues(address,"city");//only get potential city property invalid valuesInvalidValue[] validationMessages = addressValidator.getPotentialInvalidValues("city","Paris")Using the Validator frameworkHibernate 3.0.0.GA 82.4.Presentation layer validationWhen working with JSF and JBoss Seam,one can triggers the validation process at the presentation layer us-ing Seam's JSF tags <s:validate> and <s:validateAll/>,letting the constraints be expressed on the model,and the violations presented in the view<h:form><div><h:messages/></div><s:validateAll><div>Country:<h:inputText value="#{location.country}"required="true"/></div><div>Zip code:<h:inputText value="#{location.zip}"required="true"/></div><div><h:commandButton/></div></s:validateAll></h:form>Going even further,and adding Ajax4JSF to the loop will bring client side validation with just a couple ofadditional JSF tags,again without validation definition duplication.Check the JBoss Seam[http://www.jboss.com/products/seam] documentation for more information.2.5.Validation informationsAs a validation information carrier,hibernate provide an array of InvalidValue.Each InvalidValue has abuch of methods describing the individual issues.getBeanClass() retrieves the failing bean typegetBean()retrieves the failing instance (if any ie not when using getPotentianInvalidValues())getValue() retrieves the failing valuegetMessage() retrieves the proper internationalized error messagegetRootBean() retrieves the root bean instance generating the issue (useful in conjunction with @Valid),is nullif getPotentianInvalidValues() is used.getPropertyPath() retrieves the dotted path of the failing property starting fromthe root beanUsing the Validator frameworkHibernate 3.0.0.GA 9