如何在ConstraintValidator内执行@Entity查询

huangapple go评论57阅读模式
英文:

How to perform an @Entity query inside a ConstraintValidator

问题

以下是您要翻译的内容:

情景是在持久化 `Log` 实体类之前应该检查其属性 `String description` 是否包含至少一个在 `IllegalWord` 实体类中找到的单词以下是这两个实体类的映射

// 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` 实体类的 `select *` 操作我为它创建了一个存储库类

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

然后创建了 `ConstraintValidator` 验证器类该类将被 `NotContainingIllegalWords` 注解使用而该注解将用于注解 `Log` 实体类的 `String description` 字段

// 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));
        // 返回 "illegalWordRepository is null? true"
        // 即使使用了 @Autowired 注解,它仍然未被注入。

        boolean valid = true;
        
        Collection<IllegalWord> illegalWords = illegalWordRepository.findAll();
        // 在这里遇到了 NullPointerException。
        
        // valid = ...遍历 illegalWords 集合并使用正则表达式(或其他最佳方法)与 @param value 匹配,以检查它是否包含非法单词。
        
        return valid;
    }
}

我以为会像这样简单明了但是语句 `illegalWordRepository.findAll()` 引发错误因为 `illegalWordRepository` 变量为 null请注意我尝试在前面的语句中检查它是否为 `null`。

我认为我在存储库类中编写了错误的代码因此我尝试在 `@Service` 注释的类中使用 `@Autowired private IllegalWordRepository illegalWordRepository`,令人惊讶的是它在那里被正确注入了即不为 `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));
        // 返回 "illegalWordRepository is null? false"
        
        IllegalWord illegalWord = new IllegalWord();
        illegalWord.setWord(word);
        
        illegalWordRepository.save(illegalWord);
        // 在这里没有遇到 NullPointerException。

        return illegalWord;
    }
}

因此我猜测 `IllegalWordRepository` 存储库类没有问题只是它没有像我打算的那样在 `NotContainingIllegalWordsValidator` 验证器类中被注入即使我使用 `@Autowired` 注解如果这就是 `@Autowired` 注解的预期功能的话我很抱歉我是 Spring Framework 的新手)。

如果有一种在 `ConstraintValidator` 实例中执行 @Entity 查询的正确方法请告诉我

相关未回答的 SO 问题[在 Spring 4 和消息插值配置中在 ConstraintValidator 中注入 Repository][1]

**失败的尝试**

我尝试使用 `@Configurable` 注解对 `NotContainingIllegalWordsValidator` 类进行注解如下所示

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

`illegalWordRepository` 属性仍然为 `null`。

[1]: https://stackoverflow.com/q/46594706/4043409
英文:

The scenario is that before persisting a Log entity class, its property, String description should be checked if it contains at least a word found in the IllegalWord entity class. Here is the mapping of the two entity 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;
}

Since I will be performing a select * to the IllegalWord entity class, I created a repository class for it:

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

And then created the ConstraintValidator validator class that will be used by NotContainingIllegalWords annotation, that in turn, will be use to annotate the String description field of Log entity 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;
}

I thought it will be as straight-forward like that. But the statement illegalWordRepository.findAll() throws an error because the illegalWordRepository variable is null. Notice that I tried to check if it is null in the preceding statement.

I assumed that I have something wrong coded within the repository class so I attempted to used @Autowired private IllegalWordRepository illegalWordRepository inside a @Service annotated class and suprisingly it is injected there properly (e.i. not 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;
}
}

Therefore, I guess nothing is wrong with the IllegalWordRepository repository class. It's just that it is not injected in NotContainingIllegalWordsValidator validator class as I intended it to be with the @Autowired annotation (if that is how @Autowired annotation was intended to function even, I am sorry I am new in Spring Framework.).

If there is a proper approach on how to perform a @Entity query inside a ConstraintValidator instance, please tell me.

Related unanswered SO question: Inject Repository inside ConstraintValidator with Spring 4 and message interpolation configuration

<hr/>

Failed Attempt:

I tried to annotate the NotContainingIllegalWordsValidator class with @Configurable annotation, like so:

@Configurable(autowire=Autowire.BY_NAME, preConstruction=true)
public class NotContainingIllegalWordsValidator implements ConstraintValidator&lt;NotContainingIllegalWords, Object&gt; {

but the illegalWordRepository property remains null.

答案1

得分: 1

Since Your validator is not initialized by Spring, you can't inject anything into it. You'd have to access the ApplicationContext through a static variable.

在您的验证器没有由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;
  }
}

And in your ConstraintValidator:

在您的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);
    ...
  }
}
英文:

Since Your validator is not initialized by Spring, you can't inject anything into it. You'd have to access the ApplicationContext through a static variable.

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

And in your ConstraintValidator:

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

答案2

得分: 0

以下是要翻译的内容:

来自我对类似问题的回答

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

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

这是演示项目。

英文:

From my answer to a similar question:

The minimum setup for @Autowired to work properly in ConstraintValidator implementation is to have this bean in a Spring @Configuration:

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

This is the demo project

huangapple
  • 本文由 发表于 2020年8月6日 00:40:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/63269723.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定