Javax Validator多@Constraint接口报错:同一类型仅允许一个验证器
你遇到的错误提示:
HV000150: The constraint NirValidator defines multiple validators for the type NirResource: NirCalculCleConstraint, NirBrutLongueurConstraint. Only one is allowed
核心原因是Hibernate Validator的规则限制:同一个自定义约束注解,针对同一个目标校验类型,只能绑定一个验证器实现类。你现在的两个验证器NirBrutLongueurConstraint和NirCalculCleConstraint都实现了ConstraintValidator<NirValidator, NirResource>,也就是都是为NirValidator注解、校验NirResource类型服务的,这就违反了这个规则。
虽然@Constraint的validatedBy参数是数组,但它的设计目的是支持同个注解校验不同类型(比如一个注解既可以校验String,也可以校验Integer,各自对应不同的验证器),不是让同个类型用多个验证器。
下面给两种可行的解决办法:
方法一:拆分独立的约束注解
把长度校验和校验码校验拆成两个独立的自定义约束,各自绑定对应的验证器,然后在需要校验的字段上同时使用这两个注解。
1. 长度校验的注解与验证器
@Documented @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = NirBrutLongueurConstraint.class) public @interface NirLongueurValide { String message() default "NIR长度不符合要求"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } public class NirBrutLongueurConstraint implements ConstraintValidator<NirLongueurValide, NirResource> { private static final int LONGUEUR_MIN_NIR = 13; private static final int LONGUEUR_MAX_NIR = 15; @Override public boolean isValid(NirResource value, ConstraintValidatorContext context) { return value == null || value.toString().length() == LONGUEUR_MIN_NIR || value.toString().length() == LONGUEUR_MAX_NIR; } }
2. 校验码校验的注解与验证器
@Documented @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = NirCalculCleConstraint.class) public @interface NirCleValide { String message() default "NIR校验码不正确"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } public class NirCalculCleConstraint implements ConstraintValidator<NirCleValide, NirResource> { private static final int CONSTANTE_CALCUL_CLE_NIR = 97; private int calculerCle(NirResource value) { return (int) (CONSTANTE_CALCUL_CLE_NIR - ((Long.parseLong(value.toString(false))) % CONSTANTE_CALCUL_CLE_NIR)); } @Override public boolean isValid(NirResource value, ConstraintValidatorContext context) { if (value == null) return true; // 注意:原代码返回!=,这里修正为==,校验码匹配才通过 return Integer.parseInt(value.getCleNir()) == calculerCle(value); } }
3. 在字段上使用两个注解
public class SomeEntity { @NirLongueurValide @NirCleValide private NirResource nir; // 其他字段... }
方法二:合并校验逻辑到单个验证器
如果不想拆分注解,可以把两个校验逻辑合并到同一个验证器类中,在isValid方法里依次执行两个校验,只有都通过才返回true,还能分别抛出对应错误提示。
修改后的验证器和注解
@Documented @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = NirCombinedValidator.class) public @interface NirValidator { String message() default "NIR格式不符合要求"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } public class NirCombinedValidator implements ConstraintValidator<NirValidator, NirResource> { private static final int LONGUEUR_MIN_NIR = 13; private static final int LONGUEUR_MAX_NIR = 15; private static final int CONSTANTE_CALCUL_CLE_NIR = 97; private int calculerCle(NirResource value) { return (int) (CONSTANTE_CALCUL_CLE_NIR - ((Long.parseLong(value.toString(false))) % CONSTANTE_CALCUL_CLE_NIR)); } @Override public boolean isValid(NirResource value, ConstraintValidatorContext context) { if (value == null) return true; // 先校验长度 String nirStr = value.toString(); boolean lengthValid = nirStr.length() == LONGUEUR_MIN_NIR || nirStr.length() == LONGUEUR_MAX_NIR; if (!lengthValid) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("NIR长度不符合要求").addConstraintViolation(); return false; } // 再校验校验码 boolean cleValid = Integer.parseInt(value.getCleNir()) == calculerCle(value); if (!cleValid) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("NIR校验码不正确").addConstraintViolation(); return false; } return true; } }
内容的提问来源于stack exchange,提问作者Bancarel Valentin

