我正在使用Hibernate @NotNull验证器,我正在尝试创建自定义消息,告诉用户哪个字段在为null时生成了错误。像这样:
notNull.custom = The field {0} can't be null.
(这将在我的ValidationMessages.properties文件中)。
{0}应该是以这种方式传递给验证器的字段名称:
@NotNull(field="field name")
我能做到这一点吗?
答案 0 :(得分:2)
如果可以通过插入hibernate消息来满足您的要求,那么您可以像这样创建/命名您的*属性文件:
ValidationMessages.properties
在里面:
javax.validation.constraints.NotNull.message = CUSTOMIZED MESSAGE WHEN NOTNULL is violated!
默认情况下,Hibernate会搜索名为ResourceBundle
的{{1}}。还可能涉及区域设置:ValidationMessages
,ValidationMessages_en
,< ..>
Hibernate将通过ValidationMessages_de
参数提供您的自定义消息,因此将显示所有interpolatedMessage
相关信息(包括您的消息)。所以你的消息将成为真正异常的一部分。将提供一些笨拙的信息!
如果您想进行自定义异常(没有默认的ConstraintViolationException
行为),请查看以下内容:
使用ConstraintViolationException
概念,请考虑以下内容
GenericDao
制作结果:
public void saveOrUpdate(IEntity<?> entity) {
try {
if(entity.getId == null) {
em.persist(entity);
} else {
em.merge(entity)l
}
} catch(ConstraintViolationException cve) {
throw new ConstraintViolationEx(constructViolationMessage(cve.getConstraintViolations()));
}
}
private String constructMessage(Set<ConstraintViolation<?>> pConstraintViolations) {
StringBuilder customMessages = new StringBuilder();
for(ConstraintViolation<?> violation : pConstraintViolations) {
String targetAnnotation = violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName();
if(supportsCustomMessage(targetAnnotation)) {
applyMessage(violation, targetAnnotation, customMessages);
} else {
// do something with not customized constraints' messages e.g. append it to existing container
}
}
return customMessages.toString();
}
private void applyMessage(ConstraintViolation<?> pViolation, String pTargetAnnotation, StringBuilder pCustomMessages) {
String targetClass = pViolation.getRootBean().getClass().getName();
String targetField = pViolation.getPropertyPath().toString();
pCustomMessages.append(MessageFormat.format(getMessageByAnnotation(pTargetAnnotation), targetClass, targetField));
pCustomMessages.append(System.getProperty("line.separator"));
}
private String getBundleKey() {
return "ValidationMessages"; //FIXME: hardcoded - implement your true key
}
private String getMessageByAnnotation(String pTargetAnnotation) {
ResourceBundle messages = ResourceBundle.getBundle(getBundleKey());
switch(pTargetAnnotation) {
case "NotNull":
return messages.getString(pTargetAnnotation + ".message");
default:
return "";
}
}
private boolean supportsCustomMessage(String pTargetAnnotation) {
return customizedConstraintsTypes.contains(pTargetAnnotation);
}
休眠test.model.exceptions.ConstraintViolationEx
test.model.Person : name cannot be null
test.model.Person : surname cannot be null
提供有关ConstraintViolation
和root class
的相关信息。如您所见,它适用于所有hibernate支持的约束,因此您需要检查restricted field
是否可以自定义当前注释!如果它可以(它取决于你),你应该通过约束注释得到适当的消息`getMessageByAnnotation(&lt; ..&gt;)&#39;。
您需要做的就是实现不受支持的约束逻辑。例如,它可以附加它的原因消息或使用默认消息进行插值(真正的异常转到*日志文件)
答案 1 :(得分:1)
要自定义注释消息,您需要禁用isValid()方法中现有的违规消息,并构建新的违规消息并将其添加。
constraintContext.disableDefaultConstraintViolation();
constraintContext.buildConstraintViolationWithTemplate(message).addConstraintViolation();
在下面给出的示例中,我将基于“无效日期”,“不能大于今天的日期”和“日期格式正确与否”创建用于输入日期验证的注释。
@CheckDateIsValid(displayPattern = "DD/MM/YYYY", programPattern = "dd/MM/yyyy", groups = Order2.class)
private String fromDate;
注释界面-
public @interface CheckDateIsValid {
String message() default "default message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String displayPattern();
String programPattern();
}
注释实现类-
public class CheckDateIsValidValidator implements ConstraintValidator<CheckDateIsValid, String> {
@Value("${app.country.timeZone}")
private String timeZone;
private String displayPattern;
private String programPattern;
@Override
public void initialize(CheckDateIsValid constraintAnnotation) {
this.displayPattern = constraintAnnotation.displayPattern();
this.programPattern = constraintAnnotation.programPattern();
}
@Override
public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
try {
// disable existing violation message
constraintContext.disableDefaultConstraintViolation();
if (object == null) {
return true;
}
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(programPattern);
LocalDateTime time = LocalDate.parse(object, formatter).atStartOfDay();
ZoneOffset zoneOffSet = ZoneOffset.of(timeZone);
OffsetDateTime todayDateTime = OffsetDateTime.now(zoneOffSet);
if (time == null) {
customMessageForValidation(constraintContext, "date is not valid");
return false;
} else if (todayDateTime.isBefore(time.atOffset(zoneOffSet))) {
customMessageForValidation(constraintContext, "can't be greater than today date");
return false;
}
return time != null;
} catch (Exception e) {
customMessageForValidation(constraintContext, "date format should be like " + displayPattern);
return false;
}
}
private void customMessageForValidation(ConstraintValidatorContext constraintContext, String message) {
// build new violation message and add it
constraintContext.buildConstraintViolationWithTemplate(message).addConstraintViolation();
}
}