1
votes

I'm having issues while trying to define custom constraint validation with Spring.

My code is available on my GitHub.

Here the issue. I have an entity with a validation constraint on login:

@Entity
public class User {

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

  @UniqueLogin
  private String login;
  [...]
}

Here is the annotation definition:

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueLoginValidator.class)
public @interface UniqueLogin {
  String message() default "{loginIsNotUnique}";
  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};
}

And there the custom validator:

public class UniqueLoginValidator implements ConstraintValidator<UniqueLogin, String> {

   @Autowired
   private UserRepository userRepository;

   public boolean isValid(String login, ConstraintValidatorContext context) {
      return userRepository.countByLogin(login) == 0;
   }
}

When I call repository.save(new User("Kim"));, I got a NullPointerException on the injected userRepository of UniqueLoginValidator.

I assume that Spring as to inject properly the custom validator, and I have to tell him. But, I don't know how. I already tried some stuff found through Stack Overflow and Google, with no luck.

Any help will be appreciate.

2
Try to move the UserRepository to the UniqueLoginValidator 's constructor instead. Then your code will fail on init and you will see the problem. It could be e.g. scan packages problem or something else. - StanislavL
Tried to do that, but got a Caused by: java.lang.NoSuchMethodException: be.groups.sandbox.customconstraintinjection.UniqueLoginValidator.<init>() because there is no more empty constructor. I think it may be hibernate validation that works outside of Spring context that makes the repository null. - romainbsl

2 Answers

0
votes

I had similar problem, But I was able to inject Spring Bean in custom validator.

The Spring framework automatically detects all classes which implement the ConstraintValidator interface, instantiate them, and wire all dependencies.

I am using Spring Boot 2.0. Note UniqueFieldValidator is not annotated with any spring bean annotation

Sample code

public class UniqueFieldValidator implements ConstraintValidator<UniqueField, Person> {

    @Autowired
    PersionList personRepository;


    @Override
    public boolean isValid(Person object, ConstraintValidatorContext context) {
        log.info("Validating Person for Duplicate {}",object);
        return personRepository.isPresent(object);
    }

} 

@Documented
@Constraint(validatedBy = UniqueFieldValidator.class)
@Target({ ElementType.METHOD,ElementType.ANNOTATION_TYPE,ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueField {

    String message() default "Duplicate Name";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

Usage

@Component
@Validated
public class PersonService {

    @Autowired
    PersionList personRepository;

    public void addPerson(@UniqueField Person person) {
        personRepository.add(person);
    }
}
-1
votes

After few search and discussion, it seems that injecting beans into a Bean Validation Constraint is not a good practice.

That's why Hibernate context is not aware of the Spring context.