I have a custom validator that validates data against DB using repository:
@Constraint(validatedBy = DataValidator.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomValidator {
String message() default "some message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@Component
public class DataValidator implements ConstraintValidator<CustomValidator, String> {
@Autowired
private DataRepository repository;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
var data = repository.findDataByValue(value);
//validation logic with result in 'isValid' variable
return isValid;
}
}
I have entity with a field that is annotated with DataValidator:
@Entity
@Table(name = "custom_data")
public class Data {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@DataValidator
@NotBlank(message = "Value is mandatory")
@Column
private String value;
Spring Boot dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
When I call repository.save(data)
from the rest controller, my validator is called, but its repository
field is null
.
What configuration did I miss that DataRepository
bean was injected to RestController correctly, but wasn't injected into DataValidator
?
CodePudding user response:
Try it this way.
@Configuration
public class DataValidator implements ConstraintValidator<CustomValidator, String> {
private static final DataValidator holder = new DataValidator();
@Bean
public static DataValidator bean(DataRepository repository) {
holder.repository = repository;
return holder;
}
private DataRepository repository;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
var data = holder.repository.findDataByValue(value);
//validation logic with result in 'isValid' variable
return isValid;
}
}
CodePudding user response:
I found a solution that will autowire the bean, but you need to call a validator manually.
First of all, add the following to application.properties
to disable automatic validation trigger on data persistence:
spring.jpa.properties.javax.persistence.validation.mode=none
Create @Configuration class and describe Validator
bean, configure validator factory for it:
@Bean
public Validator validator(AutowireCapableBeanFactory beanFactory) {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class).configure()
.constraintValidatorFactory(new SpringConstraintValidatorFactory(beanFactory))
.buildValidatorFactory();
return validatorFactory.getValidator();
}
Add validator to the class where you want to use it and and call its validate method directly:
@RestController
public class DataController {
@Autowired
private DataRepository repository;
@Autowired
private Validator validator;
@PostMapping("/doSomething")
public Data doSomething(@RequestBody Data data) {
var validationResult = validator.validate(data);
//validation result processing
return repository.save(data);
}
}
or if you use validator within the REST endpoint as in this example, usage of the @Valid
annotation is more correct, to my mind. Then you don't need to declare Validator bean:
@RestController
public class DataController {
@Autowired
private DataRepository repository;
@PostMapping("/doSomething")
public Data doSomething(@Valid @RequestBody Data data) {
return repository.save(data);
}
}