【问题标题】:Custom validator causes StackOverflowException自定义验证器导致 StackOverflowException
【发布时间】:2019-11-11 01:54:39
【问题描述】:

我想实现ConstraintsValidator,它在注册新用户之前验证电子邮件是否可用,使用spring的依赖注入@Autowired在验证器中注入JPA Repository进行数据库搜索。

我改变了hibernate的验证器工厂,以便spring实例化验证器,这样我就可以使用@Autowired

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

注意:验证是自动完成的(我没有调用validator.validate()),因为我使用的是 REST JPA 存储库

@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);
        }
    }
}

【问题讨论】:

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


    【解决方案1】:

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

    事件是(见here):

    • BeforeCreateEvent
    • AfterCreateEvent
    • BeforeSaveEvent
    • AfterSaveEvent
    • BeforeLinkSaveEvent
    • AfterLinkSaveEvent
    • 删除前事件
    • 删除后事件

    因为您要从 db 中获取验证方法,所以您陷入了无限循环。

    您只需要用对流名称来注释验证器。

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

    并删除其他注释。

    Spring 识别出这个验证器并附加到正确的钩子上。

    欲了解更多信息,请参阅Spring Validation

    【讨论】:

    • 明确答案(y)
    猜你喜欢
    • 2015-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-16
    • 2021-02-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多