Как выполнить запрос @Entity внутри ConstraintValidator - PullRequest
3 голосов
/ 05 августа 2020

Сценарий состоит в том, что перед сохранением класса сущности 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;
}

Поскольку я буду выполнять select * с классом сущности IllegalWord, я создал для него класс репозитория:

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

Затем был создан класс валидатора ConstraintValidator, который будет использоваться аннотацией NotContainingIllegalWords, которая, в свою очередь, будет использоваться для аннотирования поля String description класса сущности Log:

// 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. Обратите внимание, что я попытался проверить, соответствует ли он null в предыдущем утверждении.

Я предположил, что у меня что-то неправильно закодировано в классе репозитория, поэтому я попытался использовать @Autowired private IllegalWordRepository illegalWordRepository внутри аннотированного класса @Service и, как ни странно, он там правильно введен (например, не 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 все в порядке. Просто он не внедряется в класс валидатора NotContainingIllegalWordsValidator, как я предполагал, с аннотацией @Autowired (если это то, как аннотация @Autowired должна была работать даже, извините, я новичок в Spring Framework. ).

Если существует правильный подход к тому, как выполнить запрос @Entity внутри экземпляра ConstraintValidator, сообщите мне.

Связанный без ответа вопрос SO: Ввести репозиторий внутри ConstraintValidator с Spring 4 и конфигурацией интерполяции сообщений

Неудачная попытка:

Я пытался аннотировать класс NotContainingIllegalWordsValidator с помощью аннотации @Configurable, например:

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

но illegalWordRepository остается имущество null.

Ответы [ 2 ]

1 голос
/ 05 августа 2020

Поскольку ваш валидатор не инициализирован Spring, вы не можете в него ничего вводить. Вам нужно будет получить доступ к ApplicationContext через переменную stati c.

@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);
    ...
  }
}
0 голосов
/ 05 августа 2020

Из моего ответа на аналогичный вопрос :

Минимальная настройка для правильной работы @Autowired в реализации ConstraintValidator - наличие этого bean-компонента в Spring @ Конфигурация:

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

Это демонстрация проект

...