Если ваша проверка зависит от нескольких полей (например, isPrimary
и либо primaryDTO
, либо secondaryDTO
), тогда единственное решение - написать пользовательский валидатор на уровне класса (UserDTO
), который будет реализовывать условную валидациюсам.
Например, создайте аннотацию:
@Documented
@Retention(RUNTIME)
@Target({ANNOTATION_TYPE, TYPE})
@Constraint(validatedBy = SecondaryValidator.class)
public @interface ValidSecondary {
String message() default "Invalid secondary";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
и создайте валидатор, который проверяет поле secondaryDTO
только тогда, когда isPrimary()
равно false
:
@Component
public class SecondaryValidator implements ConstraintValidator<ValidSecondary, UserDTO> {
private Validator validator;
public SecondaryValidator(Validator validator) {
this.validator = validator;
}
@Override
public boolean isValid(UserDTO userDTO, ConstraintValidatorContext constraintValidatorContext) {
if (userDTO.isPrimary()) {
return true;
} else {
return validator.validate(userDTO.getSecondaryDTO()).isEmpty();
}
}
}
После этого вы можете удалить аннотацию @Valid
из поля secondaryDTO
и добавить аннотацию @ValidSecondary
поверх вашей UserDTO
:
@ValidSecondary // Add this
public class UserDTO {
@Valid
private PrimaryDTO primaryDTO;
private SecondaryDTO secondaryDTO; // No more @Valid
private boolean primary;
}
Однако в этом случаеВы потеряете любое сообщение о нарушении ограничений из SecondaryDTO
, если вы хотите иметь какой-то механизм сквозного прохождения, вы можете добавить нарушения в constraintValidatorContext
в методе isValid()
, например:
Set<ConstraintViolation<SecondaryDTO>> violations = validator.validate(userDTO.getSecondaryDTO());
violations.forEach(violation -> constraintValidatorContext
.buildConstraintViolationWithTemplate(violation.getMessage())
.addConstraintViolation());