Cross field validation with HibernateValidator displays no error messages
Could you try your isValid method to be like this? (this is certainly working for me in live project):
public boolean isValid(final Object value, final ConstraintValidatorContext cvc){
boolean toReturn = false;
try{
final Object firstObj = BeanUtils.getProperty(value, firstFieldName );
final Object secondObj = BeanUtils.getProperty(value, secondFieldName );
//System.out.println("firstObj = "+firstObj+" secondObj = "+secondObj);
toReturn = firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
}
catch (final Exception e){
System.out.println(e.toString());
}
//If the validation failed
if(!toReturn) {
cvc.disableDefaultConstraintViolation();
//In the initialiaze method you get the errorMessage: constraintAnnotation.message();
cvc.buildConstraintViolationWithTemplate(errorMessage).addNode(firstFieldName).addConstraintViolation();
}
return toReturn;
}
Also I see that you are implementing the ConstraintValidator interface with an Object, literally. It should be the backing object that you have from your form:
tempBean // the one that you specify in the commandName actually.
So you implementation should like this:
implements ConstraintValidator<FieldMatch, TempBean>
This is probably not the issue here, but as a future reference, this is how it should be.
UPDATE
Inside your FieldMatch interface/annotation you have two methods : first and second, add one more called errorMessage for example:
Class<? extends Payload>[] payload() default {};
/**
* @return The first field
*/
String first();
/**
* @return The second field
*/
String second();
/**
@return the Error Message
*/
String errorMessage
Look inside you method from the Validation class - you are getting the first and second field names there., so just add the errorMessage, like this for example:
private String firstFieldName;
private String secondFieldName;
//get the error message name
private String errorMessagename;
public void initialize(final FieldMatch constraintAnnotation)
{
firstFieldName = constraintAnnotation.first();
secondFieldName = constraintAnnotation.second();
errorMessageNAme = constraintAnnotation.errorMessage();
//System.out.println("firstFieldName = "+firstFieldName+" secondFieldName = "+secondFieldName);
}
Inside isValida get it, the same way you do for first and second field name and use it.
Tiny
Just an orphan kid and have no more to say. Three things in general, cannot be avoided (at least I can never) Mother Mother-tongue Mother-land. They are always unique. I'm a family-less boy. My family was hunted leaving me all alone when my house targeted and deliberately set on a fire by a mob during a nonsense communal riot but I was survived by a rescue team with the help of firemen. As a survival, I didn't know whether it was my fortune or misfortune but when I recovered, the rescue team came to my home, one day. One of the members gave me a piece of paper in my hand in which the following text was written. lifeisnowhere. He asked me to read it carefully and I could hardly interpret the text as Life is now here, instead of Life is nowhere. All of them gave me a cute smile and went away and I decided to live peacefully and hopefully on their saying from then onwards and very soon. Because of this tragedy, I'm alone couldn't join a school but a curiosity to learn something made me a self-learner. I'm indeed a self-learner, so I'm likely not able to answer any questions on this site right now. In the field of computer science, my self-study mainly includes, QBASIC, C, C++, C#, VB, Java, JavaScript, PHP and a little about ASP.NET. Oracle, MySQL and MSSQL-Server with DBMS. and other theoretical subjects. I'm currently dealing with - Android and Java EE including Servlet, JSP-JSTL/EL (with Spring and Struts with ORM models JPA/Hibernate) and JSF.
Updated on June 17, 2022Comments
-
Tiny almost 2 years
I'm validating two fields, "password" and "confirmPassword" on the form for equality using
HibernateValidator
as specified in this answer. The following is the constraint descriptor (validator interface).package constraintdescriptor; import constraintvalidator.FieldMatchValidator; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.TYPE; import java.lang.annotation.Retention; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Target; @Target({TYPE, ANNOTATION_TYPE}) @Retention(RUNTIME) @Constraint(validatedBy = FieldMatchValidator.class) @Documented public @interface FieldMatch { String message() default "{constraintdescriptor.fieldmatch}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; /** * @return The first field */ String first(); /** * @return The second field */ String second(); /** * Defines several <code>@FieldMatch</code> annotations on the same element * * @see FieldMatch */ @Target({TYPE, ANNOTATION_TYPE}) @Retention(RUNTIME) @Documented public @interface List{ FieldMatch[] value(); } }
The following is the constraint validator (the implementing class).
package constraintvalidator; import constraintdescriptor.FieldMatch; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import org.apache.commons.beanutils.BeanUtils; public final class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> { private String firstFieldName; private String secondFieldName; public void initialize(final FieldMatch constraintAnnotation) { firstFieldName = constraintAnnotation.first(); secondFieldName = constraintAnnotation.second(); //System.out.println("firstFieldName = "+firstFieldName+" secondFieldName = "+secondFieldName); } public boolean isValid(final Object value, final ConstraintValidatorContext cvc) { try { final Object firstObj = BeanUtils.getProperty(value, firstFieldName ); final Object secondObj = BeanUtils.getProperty(value, secondFieldName ); //System.out.println("firstObj = "+firstObj+" secondObj = "+secondObj); return firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj); } catch (final Exception e) { System.out.println(e.toString()); } return true; } }
The following is the validator bean which is mapped with the JSP page (as specified
commandName="tempBean"
with the<form:form></form:form>
tag).package validatorbeans; import constraintdescriptor.FieldMatch; import javax.validation.constraints.Size; import org.hibernate.validator.constraints.NotEmpty; @FieldMatch.List({ @FieldMatch(first = "password", second = "confirmPassword", message = "The password fields must match", groups={TempBean.ValidationGroup.class}) }) public final class TempBean { @NotEmpty(groups={ValidationGroup.class}, message="Might not be left blank.") private String password; @NotEmpty(groups={ValidationGroup.class}, message="Might not be left blank.") private String confirmPassword; public interface ValidationGroup {}; //Getters and setters }
UPDATE
It's all working correctly and does the validation intended. Just one thing remains is to display the specified error message above the
TempBean
class within@FieldMatch
is not being displayed i.e only one question : how to display error messages on the JSP page when validation violation occurs?(the annotation
@NotEmpty
on both of the fieldspassword
andconfirmPassword
in theTempBean
class works and displays the specified messages on violation, the thing is not happening with@FieldMatch
).I'm using validation group based on this question as specified in this blog and it works well causing no interruption in displaying error messages (as it might seem to be).
On the JSP page these two fields are specified as follows.
<form:form id="mainForm" name="mainForm" method="post" action="Temp.htm" commandName="tempBean"> <form:password path="password"/> <font style="color: red"><form:errors path="password"/></font><br/> <form:password path="confirmPassword"/> <font style="color: red"><form:errors path="confirmPassword"/></font><br/> </form:form>
-
Tiny over 11 yearsTried and it worked, sir! Offering bounty requires some hours from now onwards, I will award it to your answer later when the system permits. Just say one additional thing please, in this method itself
cvc.buildConstraintViolationWithTemplate("Passwords in both of the fields must match.").addNode(password).addConstraintViolation();
, I have specified the error message. Is there a way to fetch the message specified in the@FieldMatch.List({})
? No matter if there is no way to do so. It's minor thing. To me, It's a great thing to know it worked according to your answer. Thank you very much -
Tiny over 11 yearsIt's all working well through out my entire application now. Thank a lot, sir!
-
Eugene over 11 years@Tiny a pleasure I could help!
-
Harmeet Singh Taara almost 10 years@Eugene is it possible this for internationalization ? \
-
Tiny over 9 years@HarmeetSinghTaara : That just requires a message key to be specified from a localized/internationalized property file replacing the actual message such as
@FieldMatch.List({@FieldMatch(first = "password", second = "confirmPassword", message = "{message.key}", groups {TempBean.ValidationGroup.class})})
as in the question (message = "{message.key}"
).