I’m working on a Spring MVC + Hibernate + JPA app with a user registration form and I decided to use a JSR-303 validator to check whether the username already existed in the DB:
public class UniqueUsernameValidator implements ConstraintValidator<VerifyUniqueUsername, String> {
@Autowired
UserService userService;
@Override
public void initialize(VerifyUniqueUsername constraintAnnotation) {
}
@Override
public boolean isValid(String username, ConstraintValidatorContext context) {
return username!=null && userService.findByUsername(username) == null;
}
}
It is very straightforward and validation worked great on my controller:
....
public String signup(@Valid @ModelAttribute("newUser") User user, BindingResult newUserBeanResult)
.....
The current problem I’m facing is that after I get my User object validated and I call:
userService.save(user);
Which implements CrudRepository, I get a NullPointerException. For some reason UserService is injected during validation on the controller but not when I call CrudRepository.save().
I saw similar posts such as this:
@Autowired bean null in ConstraintValidator when invoked by Sessionfactory.getCurrentSession.merge
and this:
hibernate validator without using autowire
but I was wondering if someone has run into this before. I would think that injecting beans to access the database on a validator is fairly common.
As a workaround I added a check for null on userService but it doesn’t feel right.
- Is this expected behaviour? Are these validations supossed to fire before calling
CrudRepository.save()? - Am I suppossed to handle “manually” hibernate events? In this case
pre-insert
I ended up solving this issue by instructing Spring’s
EntityManagerFactoryBeanto use my validator bean (more accurately, hibernate will now be using Spring’s validator):However, this threw a StackOverflow error 🙂
Apparently the cause of this issue was that my validator was using a finder method (
findByUsername) and finder methods trigger a hibernate flush, which in turn triggers a validation. This loops indefinitely until you get the most famous exception there is.So…I fixed this by changing the validators to use the EntityManager directly (instead of the CRUD repository) and temporarily change the FlushModeType to COMMIT. Here is the example:
This solves the issue where the validator was using the finder function which triggered a hibernate flush which in turn was triggering the validator causing a StackOverflowError.