自定义验证器导致StackOverflowException

时间:2019-06-29 14:11:28

标签: hibernate spring-boot spring-data-jpa spring-data check-constraints

我想实现ConstraintsValidator来验证电子邮件是否可用,然后再注册新用户,使用spring的机密性注入@Autowired在验证器中注入JPA存储库以进行数据库搜索。

我如此更改了休眠的验证器工厂,以便spring实例化验证器,以便我可以使用@Autowired

一切正常,但是就像验证进入一个无限循环,这会导致stackoverflowexception

注意:当我使用REST JPA存储库时,验证是自动完成的(我没有呼叫validator.validate()

@Getter
@Setter
@Entity
@UniqueCompteEmail
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Compte implements Serializable, UserDetails {

    private static final long serialVersionUID = -5230227676515387462L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Integer id;

    @NotBlank
    @NotNull
    @Column(unique = true)
    private String username;

    @NotNull
    @NotBlank
    @Size(min = 6)
    private String password;

    @Email
    @NotNull
    @NotBlank
    @Column(unique = true)
    private String email;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return new HashSet<GrantedAuthority>();
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return this.username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    public abstract CompteType getTypeCompte();

    public abstract void setTypeCompte(CompteType typeCompte);

    public static enum CompteType {
        ETUDIANT, ADMINISTRATEUR
    }
}

@Repository
public interface CompteRepository extends JpaRepository<Compte, Integer> {

    public Optional<Compte> findByUsername(String username);

    public Optional<Compte> findByEmail(String email);
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueCompteEmailValidator.class)
@Target({ ElementType.TYPE })
public @interface UniqueCompteEmail {

    String message() default "{com.mssmfactory.bacsimulator.uniquecompteemail.message}";

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

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

public class UniqueCompteEmailValidator implements ConstraintValidator<UniqueCompteEmail, Compte> {

    @Autowired
    private CompteRepository compteRepository;

    @Override
    public void initialize(UniqueCompteEmail constraintAnnotation) {
    }

    @Override
    public boolean isValid(Compte value, ConstraintValidatorContext context) {
        if (value != null) {
            Optional<Compte> compte = this.compteRepository.findByEmail(value.getEmail());

            return !compte.isPresent();
        } else
            return false;
    }
}

@Component
public class ValidatorAddingCustomizer implements HibernatePropertiesCustomizer {

    @Autowired
    private ValidatorFactory validatorFactory;

    public void customize(Map<String, Object> hibernateProperties) {
        if (validatorFactory != null) {
            hibernateProperties.put("javax.persistence.validation.factory", validatorFactory);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

由于您未定义任何事件,因此将为所有事件调用验证器。

事件为(请参见here):

  
      
  • BeforeCreateEvent
  •   
  • AfterCreateEvent
  •   
  • BeforeSaveEvent
  •   
  • AfterSaveEvent
  •   
  • BeforeLinkSaveEvent
  •   
  • AfterLinkSaveEvent
  •   
  • BeforeDeleteEvent
  •   
  • AfterDeleteEvent
  •   

并且由于您将从db中获取验证方法,因此您陷入了无限循环。

您只需要使用对流名称为验证者添加注释。

@Component("beforeCreateCompteValidator")
public UniqueCompteEmailValidator implements Validator<Compte>{
}

并删除其他注释。

Spring可以识别此验证器,并附加到右钩子上。

有关更多信息,请参见Spring Validation