Good evening, I'm trying to use Hibernate Validator, in the following scenario:
public class Car {
#NotNull
private String manufacturer;
#NotNull
#Size(min = 2, max = 14)
private String licensePlate;
#Min(2)
private int seatCount;
//setters and getters....
}
and I am trying to validate its attributes as follows:
public class CarMain {
public static Validator validator;
public static void main(String[] args) {
ValidatorFactory factory = Validation. buildDefaultValidatorFactory() ;
validator = factory. getValidator();
Car car = new Car(null,null,0);
Set<ConstraintViolation<Car>> st= validator.validate(car);
while(st.iterator.hasNext()){
ConstraintViolation<Car> cv = st.iterator.next();
System.out.println("Value: ("+cv.getInvalidValue()+") -->"+cv.getMessage());
System.out.println("Attribute: "+cv.getPropertyPath());
}
}
Here the whole entity is validated and the invalid values with the validation message and property path are displayed.
My question is:"Is it possible to validate only one attribute at a time with Hibernate Validator? Like I don't have to work with the whole object to validate it.
The Validator interface defines also a [Validator.validateProperty][1] method where you explicitly specify the property to validate. Mind you, you still need the object instance and you need to know the property you want to validate. This method is for example used by the integration of Bean Validation into JSF. Whether it makes sense to use it inm your case, will depend on your use case? Why don't you want to validate the whole object?
BTW, there is also Validator.validateValue which does not require an actual bean instance.
Related
I have an annotation set over objects of type dto, the same as over objects of type Entity. The annotation works on entities, but it does not work on objects of type dto.
I work in SpringBoot.
application.properties
validate.packageid.size = "The field 'PACKAGEID' can contain only {max} symbols.";
config file
#Configuration
public class ServiceConfig implements WebMvcConfigurer {
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setDefaultEncoding("UTF-8");
source.setBasename("classpath:ValidationMessages");
return source;
}
#Nullable
#Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource());
return validator;
}
}
dto
#Size(message = "{validate.packageid.size}", max = 36)
private String documentId
entity
#Column(name = "DOCUMENTID")
#Size(message = "{validate.packageid.size}", max = 36)
private String documentId;
I cannot use the annotation #Valid because I fill an object of dto type with reflection technology.
public static <S> S fillData (S object, List<Object> values){
return obtainMetadataOfObject(object, values);
}
I need to be able to get the constraint annotation, or rather its parameters, set on the fields of the dto object (but in the case of the dto object I get null, since Spring may not know what to use the constraint annotations set on the fields of the dto object), in the case of entity - it turns out, but the entity validator is engaged by Spritg, since it manages the entity as a component from the Application context.
To validate the dto on the web client side, I use the #Valid annotation in the parameters of the method that handles the request from the client
For validation dto from
Update
I put the annotation #Validation over dto and after that I got the data I need.
It work for classes that don't have classes-heir.
I get data of annotation #Size
private static int getMaxLimitSize(Field field){
field.setAccessible(true);
Size annotation = field.getAnnotation(Size.class);
int zero = 0;
if(annotation == null) return zero;
return annotation.max();
}
But this does not work for objects whose fields are split into several classes : several abstract and one produce.
Validation does not work for composite objects of type DTO, Any help is appreciated?
The validation needs to be triggered somewhere, for entities in your case the spring framework does it (or jpa perhaps). DTOs never make it there. So you have to trigger the validation (validator.validate) on your own, as per the documentation. Here's a related question asking at which application layer to do it.
#PostMapping("/test")
public void test( #RequestBody #Valid final UserDto dto) {
// logic
}
u should add #Valid annotation in controller.
If u want validate dto in service layers u should add #Validate and #Valid :
#Service
#Validated
public class Service {
public void test(#Valid UserDto dto){
// logic
}
}
I want to perform the validation of my entities in two steps. While I use a defaultValidatorFactory to validate all the fields of my entities before persisting to the database, I would like to perform a partial validation of my entities at a earlier step. But I cannot find a way to configure my validator (or validatorFactory).
Let's say I have the following class:
public class Car {
#NotNull
private String manufacturer;
#AssertTrue
private boolean isRegistered;
public Car(String manufacturer, boolean isRegistered) {
super();
this.manufacturer = manufacturer;
this.isRegistered = isRegistered;
}
}
When I do the full validation of my entity, I use the given code:
Validator validator = validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Car>> errors = validator.validate(car);
This works fine and validate both annotations NotNull and AssertTrue.
Now, I want to perform an partial validation. I mean by partial validation, I want to only validate, for example, NotNull constraints and ignore other annotations.
Is there a way to get a Validator or ValidatorFactory which uses a custom restricted list of validators?
You can find a lot of things to create your own constraint/constraint validator. In my case, I want to validate only some constraints.
Maybe I can create a custom ConstraintValidatorFactory and inject it in the Validation context? I found that we can reconfigure the context of the factory with the following code, but I don't know how to deal with it.
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validatorFactory.usingContext().constraintValidatorFactory(myCustomFactory);
For the moment, I'm lost. Someone has already done something like that? Do you have any idea how I can do this? Thanks for your time.
I'm using Java 8 and Hibernate Validator 6.0.14.
As Slaw write - use groups.
An Example
package jpatest.jpatest;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.constraints.NotNull;
public class TestApp {
/** A validation group marker */
public interface ValidationGroup1 {};
/** The bean */
public static class Bean {
// Validate for group ValidationGroup1
#NotNull(groups = ValidationGroup1.class)
private String s;
}
public static void main(String[] args) {
Bean b = new Bean();
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
// Validation without the validation group => No ConstraintViolation
Set<ConstraintViolation<Bean>> errors1 = validator.validate(b);
assert errors1.isEmpty() : "No ConstraintViolation expected";
// Validation with the validation group => 1 ConstraintViolation
Set<ConstraintViolation<Bean>> errors2 = validator.validate(b, ValidationGroup1.class);
assert errors2.size() == 1 : "1 ConstraintViolation expected";
}
}
I have a problem of triggering the custom constraint validation on jersey. I would like to activate the constraint on a method or a static method. What I have tried is putting a custom annotation and #ValidateOnExecution on top of the method, but the custom validator class still was not triggered.
#LocationIsValid
#ValidateOnExecution
public static List<Double> getLocation(String location) {
...
}
I suspected that the problem is bean annotation did not support static method, so I removed the static keyword and accessing the method by creating a new object. However the custom LocationIsValid validator still was not activated.
As a result I ended up placing a validator factory to validate this variable manually.
public static List<Double> getLocation(String location) {
...
// split the location string into a list of double
...
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
validator.validate(location, LocationIsValid.class);
}
But yet the custom constraints won't budge. I hope that someone can give me a clue on what to do next, or other suggestion on solving this issue.
More Info
It works properly when the annotation is placed on top of the resource field.
public class Product {
...
#LocationIsValid
private List<Double> location;
...
}
Updated
Even I have changed the method to normal instance method, it still doesn't work. Note that I have two overloaded methods, one is the resource getter, the other one is for converting the string to locations.
Product model
public class Product {
#Id
#JsonSerialize(using = ObjectIdSerializer.class)
private ObjectId id;
#Size(min = 5)
private String name;
#NotNull
#LocationIsValid
private List<Double> location;
private Date dateCreated;
private Date dateModified;
public Product() {
}
public List<Double> getLocation() {
return location;
}
#ValidateOnExecution
#LocationIsValid
public List<Double> getLocation(String location) {
String[] locationString = location.split(";");
if (locationString.length != 2) {
return null;
}
List<Double>locations = new ArrayList<Double>();
for (int i = 0; i < locationString.length; i++) {
locations.add(Double.parseDouble(locationString[i]));
}
return locations;
}
// Other setters getters
}
Product resource
#GET
public ProductList getProducts(#QueryParam("near") String location) {
// parse the locations variable
Product product = new Product();
// did not work
// I have placed a breakpoint on the LocationIsValid
List<Double> locations = product.getLocation(location);
}
Note: I am pretty sure the problem is not located in LocationIsValid, as it works correctly when I validated it as an entity #Valid Product product I am using Jersey 2.4.1 with jersey-bean-validation 2.4.1 dependency
As you say validation of static methods is not supported by the Bean Validation specification. The validation of instance methods should work though, can you post the entire code of your JAX-RS resource? Is getLocation() defined on that resource? JAX-RS should trigger the validation of method constraints (be it parameter or return value constraints) on the methods of resource classes.
The problem is I did not actually invoke the validator using #Valid at the Product object in the controller. The reason is I only need to validate the location variable and not the whole Product object. So I guess the only way to solve it is to invoke the validator factory programatically inside the resource function. Note that the reason I did not success validate the property using the validator before is because I never call the correct method. Below is the code that make use of validation factory to validate a specific property in a resource object.
#GET
public ProductList getProducts(#QueryParam("near") String location) {
// parse the locations variable
List<Double> locations = Product.getLocation(location);
// validate the value
Set<ConstraintViolation<Product>> constraints = Validation
.buildDefaultValidatorFactory()
.getValidator()
.validateValue(Product.class, "location", locations, Default.class);
if (locations == null || locations.size() != 2
|| constraints.size() > 0) {
throw new ConstraintViolationException(constraints);
}
}
If someone has better ideas or suggestions to workaround with this issue, please let me know.
I have a class with hibernate's validation annotation on some fields (such as #NotNull and #Size(min = 4, max = 50), etc...)
public class MyClass {
Long id;
#NotEmpty
#Size(min = 4, max = 50)
String machineName;
#NotEmpty
#Size(min = 4, max = 50)
String humanName;
// Getters, setters, etc…
}
I also have a custom controller that acts as a JSON API, and a JSON deserializer that creates MyClass objects when API methods are called. In my custom controller I have a method to create a new object of that type:
#RequestMapping(method = RequestMethod.POST)
public long createMyObject(#RequestBody #Valid MyClass newObj) {
// Create the object in the database
return newObj.getId();
}
and another method that updates an existing object
#RequestMapping(method = RequestMethod.PUT)
public void updateMyObject(#RequestBody MyClass updatedObj) {
MyClass existingObj = // Get existing obj from DB by updatedObj.getId();
// Do some secondary validation, such as making sure that a specific
// field remains unchanged compared to the existing instance
if (existingObj.getMachineName() != null &&
!existingObj.getMachineName().equals(updatedObj.getMachineName())) {
throw new CannotChangeMachineNameException();
}
else {
updatedObj.setMachineName(existingObj.getMachineName());
}
// [HERE IS WHERE I WANT THE MAGIC TO HAPPEN]
// Save updatedObj to the database
}
While I can use #Valid in createMyObject, I cannot use it in updateMyObject because our API implementation requires that machineName remains unchanged - users can call the API with a JSON object that either excludes machineName entirely or populate it with the same value that exists in the database.*
Before saving the updated object to the database I want to call the same validator that having the #Valid annotation would cause to be called. How can I find this validator and use it?
Nothing says you need to use #Valid in your controller methods only. Why not make a validation method that accepts a parameter you annotate as #Valid, then just return that same parameter.
Like this:
public Book validateBook(#Valid Book book) {
return book;
}
Looks like an alternative would be to use Hibernate's validation package. Here's it's documentation.
Basically, you get a Validator from a ValidationFactory, and then use the validator like this:
#Test
public void manufacturerIsNull() {
Car car = new Car(null, "DD-AB-123", 4);
Set<ConstraintViolation<Car>> constraintViolations =
validator.validate(car);
assertEquals(1, constraintViolations.size());
assertEquals("may not be null", constraintViolations.iterator().next().getMessage());
}
I am looking at using Hibernate Validator for a requirement of mine. I want to validate a JavaBean where properties may have multiple validation checks. For example:
class MyValidationBean
{
#NotNull
#Length( min = 5, max = 10 )
private String myProperty;
}
But if this property fails validation I want a specific error code to be associated with the ConstraintViolation, regardless of whether it failed because of #Required or #Length, although I would like to preserve the error message.
class MyValidationBean
{
#NotNull
#Length( min = 5, max = 10 )
#ErrorCode( "1234" )
private String myProperty;
}
Something like the above would be good but it doesn't have to be structured exactly like that. I can't see a way to do this with Hibernate Validator. Is it possible?
You could create a custom annotation to get the behaviour you are looking for and then on validating and using refelection you could extract the value of the annotation. Something like the following:
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface ErrorCode {
String value();
}
In your bean:
#NotNull
#Length( min = 5, max = 10 )
#ErrorCode("1234")
public String myProperty;
On validating your bean:
Set<ConstraintViolation<MyValidationBean>> constraintViolations = validator.validate(myValidationBean);
for (ConstraintViolation<MyValidationBean>cv: constraintViolations) {
ErrorCode errorCode = cv.getRootBeanClass().getField(cv.getPropertyPath().toString()).getAnnotation(ErrorCode.class);
System.out.println("ErrorCode:" + errorCode.value());
}
Having said that I probably would question the requirements for wanting error codes for these types of messages.
From the section 4.2. ConstraintViolation of the specification:
The getMessageTemplate method returns the non-interpolated error message (usually the message attribute on the constraint declaration). Frameworks can use this as an error code key.
I think this is your best option.
What I would try to do is isolate this behavior on the DAO Layer of the application.
Using your example we would have:
public class MyValidationBeanDAO {
public void persist(MyValidationBean element) throws DAOException{
Set<ConstraintViolation> constraintViolations = validator.validate(element);
if(!constraintViolations.isEmpty()){
throw new DAOException("1234", contraintViolations);
}
// it's ok, just persist it
session.saveOrUpdate(element);
}
}
And the following exception class:
public class DAOException extends Exception {
private final String errorCode;
private final Set<ConstraintViolation> constraintViolations;
public DAOException(String errorCode, Set<ConstraintViolation> constraintViolations){
super(String.format("Errorcode %s", errorCode));
this.errorCode = errorCode;
this.constraintViolations = constraintViolations;
}
// getters for properties here
}
You could add some annotation information based on what property has not validated from here, but always doing this on the DAO method.
I hope this helped.