I\'m doing a lot of our validation with Hibernate and Spring Annotations like so:
public class Account {
@NotEmpty(groups = {Step1.class, Step2.class})
And also:
@Autowired
@Qualifier("mvcValidator")
Validator validator;
...
violations = validator.validate(account);
Going a step further than Jaiwo99 in his answer:
// org.springframework.validation.SmartValidator - implemented by
// LocalValidatorFactoryBean, which is funny as it is not a FactoryBean per se (just saying)
@Autowired
SmartValidator validator;
public String saveAccount(@ModelAttribute Account account, BindingResult result) {
// ... custom logic
validator.validate(account, result, Account.Step1.class);
if (result.hasErrors()) {
// ... on binding or validation errors
} else {
// ... on no errors
}
return "";
}
And the mandatory link to SmartValidator JavaDoc if you are interested.
Adding to answered by @digitaljoel, you can throw the ConstraintViolationException once you got the set of violations.
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<NotionalProviderPaymentDTO>> violations = validator.validate( notionalProviderPaymentDTO );
if(!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
You can create your own exception mapper which will handle ConstraintViolationException and send the errors messages to the client.
If you have everything correctly configured, you can do this:
@Autowired
Validator validator;
Then you can use it to validate you object.
Here is a code sample from JSR 303 spec
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Driver driver = new Driver();
driver.setAge(16);
Car porsche = new Car();
driver.setCar(porsche);
Set<ConstraintViolation<Driver>> violations = validator.validate( driver );
So yes, you can just get a validator instance from the validator factory and run the validation yourself, then check to see if there are violations or not. You can see in the javadoc for Validator that it will also accept an array of groups to validate against.
Obviously this uses JSR-303 validation directly instead of going through Spring validation, but I believe spring validation annotations will use JSR-303 if it's found in the classpath
This link gives pretty good examples of using validations in Spring apps. https://reflectoring.io/bean-validation-with-spring-boot/
I have found an example to run the validation programmitically in this article.
class MyValidatingService {
void validatePerson(Person person) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Person>> violations = validator.validate(person);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}
It throws 500
status, so it is recommended to handle it with custom exception handler.
@ControllerAdvice(annotations = RestController.class)
public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<CustomErrorResponse> constraintViolationException(HttpServletResponse response, Exception ex) throws IOException {
CustomErrorResponse errorResponse = new CustomErrorResponse();
errorResponse.setTimestamp(LocalDateTime.now());
errorResponse.setStatus(HttpStatus.BAD_REQUEST.value());
errorResponse.setError(HttpStatus.BAD_REQUEST.getReasonPhrase());
errorResponse.setMessage(ex.getMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
}
Second example is from https://www.mkyong.com/spring-boot/spring-rest-error-handling-example/
Update: Using validation is persistence layer is not recommended: https://twitter.com/odrotbohm/status/1055015506326052865