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
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<NotContainingIllegalWords, Object> {
but the illegalWordRepository
property remains null
.
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<NotContainingIllegalWords, Object> {
public boolean isValid(String value, ConstraintValidatorContext cxt) {
ApplicationContext applicationContext = MyApplication.getApplicationContext();
IllegalWordRepository illegalWordRepository = applicationContext.getBean(IllegalWordRepository.class);
...
}
}