2

I try to create my validate annotation.

When validation fails it's work good. I get a message about. But when validation passes I get RollbackException.

What I do in my validate annotation?

I want to check a unique email in the user table. Therefore, I have created @annotation:

package ru.project.domain.validation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Constraint(validatedBy = UniqueEmailValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface UniqueEmail {

    public String message() default "e-mail exist!";

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

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

}

I have created Validator:

package ru.project.domain.validation;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.springframework.beans.factory.annotation.Autowired;

import ru.project.service.UserService;

public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {

    @Autowired
    private UserService userService;

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && !userService.isEmailAlredyUse(value);
    }

}

When I enter not-unique email I get message "e-mail exist!"

But when I enter unique email I get error:

2019-07-07 13:14:09.770 ERROR 11680 --- [io-8080-exec-10] o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [HV000028: Unexpected exception during isValid call.] 2019-07-07 13:14:09.777 ERROR 11680 --- [io-8080-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction] with root cause

java.lang.NullPointerException: null at ru.project.domain.validation.UniqueEmailValidator.isValid(UniqueEmailValidator.java:17) ~[classes/:na] at ru.project.domain.validation.UniqueEmailValidator.isValid(UniqueEmailValidator.java:1) ~[classes/:na] at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:171) ~[hibernate-validator-6.0.17.Final.jar:6.0.17.Final] at org.hibernate.validator.internal.engine.constraintvalidation.SimpleConstraintTree.validateConstraints(SimpleConstraintTree.java:68) ~[hibernate-validator-6.0.17.Final.jar:6.0.17.Final] at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:73) ...

The function isValid is called twice. When validated after push button on the form and when userRepository try to save new User.

I will show what debugger shows me every time.

When push button on the form(unique email): enter image description here

When userRepository try to save new User: enter image description here

UPDATE: isEmailAlreadyUse:

public boolean isEmailAlredyUse(String value) {
        User user = userRepository.findByEmail(value);
        if(user != null) {
            return true;
        }
        return false;
    }

During the second run userService = null: enter image description here

2 Answers2

2

One decision is to turn off a check when 'hibernate' save an entity in the database. To do this you need to set next property in your application.properties

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

0

Try this:

ExtendedEmailValidator.java:

@Email(message="Please provide a valid email address")
@Pattern(regexp=".+@.+\\..+", message="Please provide a valid email address")
@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {})
@Documented
public @interface ExtendedEmailValidator {
    String message() default "Please provide a valid email address";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

How to use it? In your entity add this:

    @Column
    @ExtendedEmailValidator(message = "custom message")
    private String email;

in your @Controller try this:

@PostMapping
public ModelAndView createNewUser(@Valid @ModelAttribute("user") User user, BindingResult bindingResult) {
ModelAndView modelAndView = new ModelAndView();
User userExists = userService.findUserByEmail(user.getEmail());

if (userExists != null) {
  bindingResult
               .rejectValue("email", "error.user",
                            "There is already a user registered with the email provided");
}

if(bindingResult.hasErrors()){
    modelAndView.setViewName("registration");
    return modelAndView;
}

/*
Other code
*/
}
Community
  • 1
  • 1
Antonio112009
  • 415
  • 2
  • 7
  • 21
  • If I don't mistake Controller should run without annotation because all logic of check is presented in @Controler. Info about the error is added manually. – Alexander Lopatin Jul 07 '19 at 15:16