HV000028: Unexpected exception during isValid call

Viewed 2516

I created a custom validation rule to check if a username exists in the database.

My User class has a username that has a custom validation rule that when an object is created it checks in the database if that same username exists.

I use an interface UserRepository extends JpaRepository<User, Integer> to save the users to the database and in the custom validation rule to check if the username already exists. I make use of

@Autowired
UserRepository userRepository;

Separately I can validate users and save them to the database, but when using them together like calling userRepository.save(user); the @Autowired UserRepository userRepository; in the custom validation rule does not get initiated so it remains null.

javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.

Custom validation rule

public class UsernameConstraintValidator implements ConstraintValidator<Username, String> {
    
    @Autowired
    UserRepository userRepository;

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        return !userRepository.existsUserByUsername(s);
    }
}

Trying to save user in the controller

  @PostMapping("/save")
    public String save(@Valid @ModelAttribute("user") User user,BindingResult result, @RequestParam String password2){

        if(result.hasErrors()) return "register";

       userRepository.save(user);
               
        return "login";
    }

An illustration of my problem

2 Answers

Add spring.jpa.properties.javax.persistence.validation.mode=none in application.propperties

I found the answer in this thread: Spring-Boot How to properly inject javax.validation.Validator

This answer includes the solution to the problem that I was having with bean validation and hibernate.

"...because Hibernate doesn't know about the Spring Context and as far as I can tell there is no way to tell it, not even with the LocalValidatorFactoryBean. This causes the Validator's to run twice. One correctly, and once that fails."

In short the solution is to tell hibernate to not run the validation by adding the following line in your application.propperties

spring.jpa.properties.javax.persistence.validation.mode=none

I suggest you read the full answer of this question to better understand it's implications with LocalValidatorFactoryBean.

Credits also go to João Dias for helping me find this answer.

Your Custom Validation class does not seem a Spring managed bean. Try adding @Component annotation to it as follows:

@Component
public class UsernameConstraintValidator implements ConstraintValidator<Username, String> {
    
    @Autowired
    UserRepository userRepository;

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        return !userRepository.existsUserByUsername(s);
    }
}

Update 06/09/2021 As it seems (according to https://stackoverflow.com/a/13361762/16572295) you also need to setup a LocalValidatorFactoryBean to have this working:

@Bean
public Validator validatorFactory() {
    return new LocalValidatorFactoryBean();
}
Related