You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义ConstraintValidator引发StackOverflowException问题求助

解决自定义ConstraintValidator引发的StackOverflowException问题

你遇到的无限循环栈溢出问题,核心原因是校验逻辑和Hibernate的实体生命周期校验形成了闭环调用:当Spring Data REST触发实体保存时,Hibernate会自动执行Bean Validation(也就是你的@UniqueCompteEmail校验);而在校验器中调用compteRepository.findByEmail()查询时,Hibernate加载返回的Compte实体又会再次触发该实体的所有校验,如此往复直到栈被撑爆。

下面是具体的解决方案,分步骤帮你打破这个循环:

1. 改用EntityManager查询并禁用查询时的校验

把校验器里的CompteRepository注入替换为EntityManager,并在查询时添加提示禁用Bean Validation,避免查询过程中再次触发实体校验:

public class UniqueCompteEmailValidator implements ConstraintValidator<UniqueCompteEmail, Compte> {
    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public void initialize(UniqueCompteEmail constraintAnnotation) {}

    @Override
    public boolean isValid(Compte value, ConstraintValidatorContext context) {
        if (value == null || value.getEmail() == null) {
            return false;
        }

        // 直接用EntityManager执行查询,同时禁用校验避免循环
        TypedQuery<Long> query = entityManager.createQuery(
            "SELECT COUNT(c) FROM Compte c WHERE c.email = :email", Long.class);
        query.setParameter("email", value.getEmail());
        // 关键:设置查询提示,让Hibernate查询时不触发Bean Validation
        query.setHint("javax.persistence.validation.mode", "NONE");

        Long count = query.getSingleResult();
        return count == 0;
    }
}

2. 优化校验逻辑(区分新建/更新场景)

额外补充一个优化点:如果是更新实体(id不为null),只有当邮箱发生变更时才需要校验唯一性,避免不必要的查询:

@Override
public boolean isValid(Compte value, ConstraintValidatorContext context) {
    if (value == null || value.getEmail() == null) {
        return false;
    }

    // 更新场景:检查邮箱是否和原有记录一致,一致则无需校验
    if (value.getId() != null) {
        Compte existing = entityManager.find(Compte.class, value.getId());
        if (existing != null && value.getEmail().equals(existing.getEmail())) {
            return true;
        }
    }

    // 新建或邮箱变更场景:校验唯一性
    TypedQuery<Long> query = entityManager.createQuery(
        "SELECT COUNT(c) FROM Compte c WHERE c.email = :email", Long.class);
    query.setParameter("email", value.getEmail());
    query.setHint("javax.persistence.validation.mode", "NONE");

    Long count = query.getSingleResult();
    return count == 0;
}

3. 移除不必要的Validator配置

在Spring Boot中,默认已经支持让Hibernate Validator使用Spring管理的实例,你的ValidatorAddingCustomizer可能属于冗余配置,甚至可能引发双重校验触发的问题。可以尝试移除这个类,看看依赖注入是否依然正常工作——如果没问题,就彻底删掉它,减少配置复杂度。

内容的提问来源于stack exchange,提问作者Sidi Mohamed MOULEY SLIMANE

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:40:06