0

Does this type of validation work?

Annotation:

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

Validator:

@Component
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
  @Autowired
  private UserRepository userRepository;

  @Override
  public boolean isValid(String value, ConstraintValidatorContext context) {
      return userService.isEmailUnique(value); // read as a call to userRepository.findByEmail(emailAddress)
  }
}

And the entity

@Entity
public class User {
  ...
  @UniqueEmail
  private String email;
}

It fails because of the recursive calls between isValid() method and userRepository.findByEmail(). It this correct behavior? Does the findByEmail always create a new User and apply the validation on it?

update:
A part of the stacktrace:

java.lang.StackOverflowError: null
...
(many times)
...
UserService.isEmailUnique(UserService.java:84)
...
UniqueEmailValidator.isValid(UniqueEmailValidator.java:29)
UniqueEmailValidator.isValid(UniqueEmailValidator.java:13)

The property spring.jpa.properties.javax.persistence.validation.mode=none resolves this. But still it was not even a double validation.

IgorZ
  • 1,125
  • 3
  • 20
  • 44

2 Answers2

0

Answering on my own question.

Thanks for this hint:

In validation, you are doing a query on the same entity which means before doing the query, hibernate need to flush what is queued in your session.

In other words if I got it right, in this case hibernate saves before the validation with it's query.

So I've added the same propagation for my method:

@Transactional(propagation = Propagation.NOT_SUPPORTED)
Optional<User> findByEmail(String email);

It works.
Also I don't need to keep the spring.jpa.properties.javax.persistence.validation.mode=none anymore.

IgorZ
  • 1,125
  • 3
  • 20
  • 44
0

You shouldn't use find with entities just to check if an email address exists in

userService.isEmailUnique(value)

Why don't you create a method:

int countByEmail(String email)
Simon Martinelli
  • 34,053
  • 5
  • 48
  • 82
  • In my case I had to find a user in both cases: when I `post/create new` , `patch/edit existing user`. So besides the email I also checked the `id` (or does the user exist). – IgorZ Jul 06 '21 at 12:50
  • 1
    But this can be all done in one query. no need to load an entity – Simon Martinelli Jul 06 '21 at 12:51