如何在 ConstraintValidator 中执行 @Entity 查询

How to perform an @Entity query inside a ConstraintValidator

场景是,在持久化 Log 实体 class 之前,应检查其 属性、String description 是否至少包含在 IllegalWord实体class。下面是两个实体classes:

的映射
// Log.java
@Entity
public class Log {
    @GeneratedValue(strategy=GenerationType.SEQUENCE)
    @Id
    private Long id;

    @NotContainingIllegalWords
    private String description;
}

// IllegalWord.java
@Entity
public class IllegalWord {
    @GeneratedValue(strategy=GenerationType.SEQUENCE)
    @Id
    private Long id;
    private String word;
}

由于我将对 IllegalWord 实体 class 执行 select *,因此我为其创建了一个存储库 class:

// IllegalWordRepository.java
@Repository
public interface IllegalWordRepository extends CrudRepository<IllegalWord, Long> {}

然后创建 ConstraintValidator 验证器 class 将由 NotContainingIllegalWords 注释使用,反过来,将用于注释 String description 字段 Log 实体 class:

// NotContainingIllegalWordsValidator.java
public class NotContainingIllegalWordsValidator implements ConstraintValidator<NotContainingIllegalWords, Object> {
    private static final Logger log = LoggerFactory.getLogger(NotContainingIllegalWordsValidator.class);

    @Autowired
    private IllegalWordRepository illegalWordRepository;
    
    public void initialize(NotContainingIllegalWords constraintAnnotation) {}

    public boolean isValid(String value, ConstraintValidatorContext cxt) {
        log.debug("illegalWordRepository is null? " + (illegalWordRepository == null));
        // Returns "illegalWordRepository is null? true"
        // It is not injected even with the @Autowired annotation.

        boolean valid = true;
        
        Collection<IllegalWord> illegalWords = illegalWordRepository.findAll();
        // Encounters a NullPointerException here.
        
        // valid = ...loop through illegalWords collection and match regex (or whatever optimal approach)
        // with @param value to check if it contains the illegal word.
        
        return valid;
    }

我以为它会像那样直截了当。但是语句 illegalWordRepository.findAll() 会抛出错误,因为 illegalWordRepository 变量为空。请注意,我试图在前面的语句中检查它是否为 null

我假设我在存储库 class 中编码有问题,所以我尝试在带注释的 @Service 中使用 @Autowired private IllegalWordRepository illegalWordRepository class 并且令人惊讶的是它被注入到那里正确(e.i。不是null):

// IllegalWordService.java
@Service
public class IllegalWordService {
    private static final Logger log = LoggerFactory.getLogger(IllegalWordService.class);
    
    @Autowired
    private IllegalWordRepository illegalWordRepository;

    public IllegalWord generate(String word) {
        log.debug("illegalWordRepository is null? " + (illegalWordRepository == null));
        // Returns "illegalWordRepository is null? false"
        
        IllegalWord illegalWord = new IllegalWord();
        illegalWord.setWord(word);
        
        illegalWordRepository.save(illegalWord);
        // Didn't encounter a NullPointerException here.

        return illegalWord;
    }
}

因此,我想 IllegalWordRepository 存储库 class 没有任何问题。只是它没有注入 NotContainingIllegalWordsValidator 验证器 class 中,因为我打算将它与 @Autowired 注释一起使用(如果这就是 @Autowired 注释的预期功能,对不起,我是 Spring Framework 的新手。)。

如果有关于如何在 ConstraintValidator 实例中执行 @Entity 查询的正确方法,请告诉我。

相关未回答的 SO 问题:Inject Repository inside ConstraintValidator with Spring 4 and message interpolation configuration


尝试失败:

我尝试用 @Configurable 注释对 NotContainingIllegalWordsValidator class 进行注释,如下所示:

@Configurable(autowire=Autowire.BY_NAME, preConstruction=true)
public class NotContainingIllegalWordsValidator implements ConstraintValidator<NotContainingIllegalWords, Object> {

illegalWordRepository 属性 仍然是 null

来自我的 answer to a similar question:

@Autowired 在 ConstraintValidator 实现中正常工作的最小设置是将此 bean 放在 Spring @Configuration:

@Bean
public Validator defaultValidator() {
    return new LocalValidatorFactoryBean();
}

这是demo项目

因为你的验证器没有被 Spring 初始化,你不能向它注入任何东西。您必须通过静态变量访问 ApplicationContext

@SpringBootApplication
public class MyApplication {

  private static ApplicationContext applicationContext;

  public static void main(final String[] args) {
    applicationContext = SpringApplication.run(MyApplication.class, args);
  }
  public static ApplicationContext getApplicationContext() {
    return applicationContext;
  }
}

在你的ConstraintValidator中:

public class NotContainingIllegalWordsValidator implements ConstraintValidator<NotContainingIllegalWords, Object> {
  public boolean isValid(String value, ConstraintValidatorContext cxt) {
    ApplicationContext applicationContext = MyApplication.getApplicationContext();
    IllegalWordRepository illegalWordRepository = applicationContext.getBean(IllegalWordRepository.class);
    ...
  }
}