Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConstraintValidator dependency injection leads to ValidationException when being validated at class level

I've encountered an unexpected behaviour when using dependency injection in a ConstraintValidator which is getting evaluated at class level.

Entity class:

@Entity
@ValidDemoEntity
public class DemoEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

}

Validation annotation:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {DemoEntityValidator.class})
public @interface ValidDemoEntity {

    String message() default "{some.demo.validator.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

}

Validator:

public class DemoEntityValidator implements ConstraintValidator<ValidDemoEntity, DemoEntity> {

    private DemoEntityRepository demoEntityRepository;

    public DemoEntityValidator(DemoEntityRepository demoEntityRepository) {
        this.demoEntityRepository = demoEntityRepository;
    }

    @Override
    public void initialize(ValidDemoEntity constraintAnnotation) {

    }

    @Override
    public boolean isValid(DemoEntity demoEntity, ConstraintValidatorContext constraintValidatorContext) {
        return true;
    }
}

Test class:

@SpringBootTest
public class ValidatorInstantiationTest {

    private Validator validator;

    @Before
    public void setUp() throws Exception {
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
        validator = validatorFactory.getValidator();
    }

    @Test
    public void shouldInitiateAndCallDemoEntityValidator() {
        DemoEntity demoEntity = new DemoEntity();
        validator.validate(demoEntity);
    }

}

Validating the entity leads to:

javax.validation.ValidationException: HV000064: Unable to instantiate ConstraintValidator: com.example.demo.DemoEntityValidator.

and further down the stack trace:

Caused by: java.lang.NoSuchMethodException: com.example.demo.DemoEntityValidator.<init>()

which indicates that Hibernate tried to initiate the the class instead of letting Spring take care of that.

The strange thing about this is that dependency injection works fine for validations applied on field level.

The code is available at GitHub.

like image 475
Thomas Schmidt Avatar asked Jun 27 '19 13:06

Thomas Schmidt


People also ask

How do you validate query parameters in spring boot?

Validating a PathVariable Just as with @RequestParam, we can use any annotation from the javax. validation. constraints package to validate a @PathVariable. The default message can be easily overwritten by setting the message parameter in the @Size annotation.


1 Answers

The exception says that there is no default constructor because Hibernate Validator tries to instantiate your validator.

You have to use Spring.

1 Make your validator a Spring Bean:

@Component
public class DemoEntityValidator implements ConstraintValidator<ValidDemoEntity, DemoEntity> {

2 Inject the Spring provided validator and use the SpringRunner for executing your tests:

@SpringBootTest
@RunWith(SpringRunner.class)
public class ValidatorInstantiationTest {

    @Autowired
    private Validator validator;

    @Test
    public void shouldInitiateAndCallDemoEntityValidator() {
        DemoEntity demoEntity = new DemoEntity();
        validator.validate(demoEntity);
    }

}
like image 74
Simon Martinelli Avatar answered Oct 12 '22 10:10

Simon Martinelli