9

I have a custom bean validator which checks if a given field on an entity is unique for some conditions. If the validation fails, the message should include a field (e.g. the ID) of the already existing entity. So for example the message should be:

"Product 42 already has such a value defined, choose a unique value."

Is this possible using bean validation?

AFAICS, the message format may include parameters, such as:

"Length must be between {min} and {max}."

But this can only reference the "static" attributes of the validation annotation, in this case:

@Size(min=1, max=16)
private String name;

In my case, the value is only known within isValid of my custom validator.

0

2 Answers 2

3

You are right!, And for what you want!, you can build constraint violation message inside the isValid() method. For this the constraints Annotation should be specific for particular class on which it has been applied and it should be a class level validation constraints. Inside isValid before returning false on failure of validation you can create message consisting value of class instance. For example:

@check class Test{ int id; @validations...on fields}.

public boolean isValid(Test value, ConstraintValidatorContext context) 
{
// your check logic
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("It should be different for(custom message) .."+ value.id).addConstraintViolation();
return false; // based on constraint filure.

}

But i think you want to do this with Field level annotations! I don't have idea about that looking forward to your results.

Sign up to request clarification or add additional context in comments.

2 Comments

The problem is that I don't want to construct the final message in the validator, only a format (for translations) and parameters. But I guess I'm supposed to localize the message inside the validator.
I posted the solution we ended up with as another answer. But I think your answer allows for more specific error messages, even if localization has to be done inside the validator, I accepted it.
0

It's not really the nicest solution, but what we ended up doing was adding something like the following to our top-level exception handling code:

String getConstraintViolationMessages(ConstraintViolationException e) {
    StringBuilder sb = new StringBuilder();
    for (ConstraintViolation<?> violation : e.getConstraintViolations()) {
        sb.append(getMessage(violation));
        sb.append("\n");
    }
    sb.setLength(sb.length() - 1);
    return sb.toString();
}

String getMessage(ConstraintViolation<?> violation) {
    String key = violation.getMessageTemplate();
    String messageFormat = localize(key);

    Object entity = violation.getRootBean();
    String identifier;
    if (entity instanceof PrimaryKeyed) {
        identifier = String.valueOf(((PrimaryKeyed) entity).getId());
    } else {
        identifier = entity.toString();
    }

    return MessageFormat.format(messageFormat, identifier);
}

Note that PrimaryKeyed is a custom interface that is used on our entities. We also have some other interfaces and custom handling not shown above.

2 Comments

In which class are these two methods sitting ? My custom constraint with a context.buildConstraintViolationWithTemplate("{no.mobitroll.rest.resources.entities.beans.validators.QuizType.enum}").addConstraintViolation(); does not interpolate the value yet.
@Stephane We have them in a top-level exception handler class, which is probably a good thing to have regardless. Where you add this really depends on your architecture and what frameworks you use, so I can't really give a more specific answer unfortunately.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.