JSR-303 How to validate two children objects differently - java

I'm using Hibernate Validator as JSR-303 implementation.
Suppose I have:
class Form {
#Valid
private Owner mainOwner;
#Valid
private Owner secondOwner;
// ... many other fields
}
class Owner {
#DriverLicenseValid // some custom validation
private String driverLicense;
// ... many other fields
}
// ...
Form myForm;
validator.validate(myForm);
See, I want to validate the whole form, it contains two similar child objects of type Owner. Problem is that for mainOwner driverLicense should be #NotNull while for secondOwner it should be #Null.
I can't use groups here (can I?) since the whole model is checked by a single validate() call. Is there any way to solve this without writing this rule as a custom constraint on the Form level?

Create a validation annotation on the mainOwner field in Form that validates that the Owner instance has a set driver license.
The #DriverLicenseValid annotation should just follow the suggestions from the jsr-303 spec that null values are valid by default. So you don't need to change the Owner class.

If you work with Hibernate Validator 4.x, you could use a custom group sequence provider as described in this forum post.

Use groups:
class Form {
#Valid
#ConvertGroup(from=Default.class, to=NotNullDriversLicense.class)
private Owner mainOwner;
#Valid
#ConvertGroup(from=Default.class, to=NullDriversLicense.class)
private Owner secondOwner;
// ... many other fields
}
class Owner {
#Null(groups = NullDriversLicense.class)
#NotNull(groups = NotNullDriversLicense.class)
private String driverLicense;
// ... many other fields
}
interface NullDriversLicense {}
interface NotNullDriversLicense {}
// ...
Form myForm;
validator.validate(myForm);

Related

How to hardcode group information on field for cascading validation

I have the following bean;
public class Customer {
#NotNull(groups = New.class)
private String id;
#Valid
private List<CustomerDetail> detailList;
}
As you see, I cascade validation down to each CustomerDetail in detailList by annotating the field with #Valid, but I wish to propagate the validation with a hard-coded group, is that possible? Whatever group is supplied for validation, I wish a fixed group, namely New to be active in validation of detailList.
This is due to my conflicting requirements, one wishes to treat details as a sub-resource of Customer therefore I need full validation on it all the time when it is validated within a customer pojo. Another requirement is to treat each detail as a separate resource, therefore I need to do patch for some fields, so when it is validated separately, different groups can be applied.
public class CustomerDetail {
#NotNull(groups = New.class)
private String desc;
private String remark;
}
So when it is any sort of operation for Customer, every CustomerDetail in customerList should use New group, even if Customer does not necessarily use that group for validation.
In a way, I want to do this;
public class Customer {
#NotNull(groups = New.class)
private String id;
#Validated(New.class)
private List<CustomerDetail> detailList;
}
But I was unable to find such a feature, I wanted to do this to evade creating multiple groups, which was deemed confusing.
You need to introduce your own annotation to have class level constraints. Create a custom annotation with own validation logic implemented in the validator.
See the chapter 6.2. Class-level constraints of the doc
Or see the example

Multiple constraints spring validation

I am using spring to validate a form. The model for the form is similar to this:
public class FormModel {
#NotBlank
private String name;
#NotNull
#ImageSizeConstraint
private MultipartFile image;
}
The '#ImageSizeConstraint' is a custom constraint. What I want is for the #NotNull to be evaluated first and if this evaluates to false, not to evaluate #ImageSizeConstraint.
If this is not possible, I will have to check for null in the custom constraint as well. Which is not a problem, but I would like to seperate the concerns (not null / image size / image / aspectratio / etc).
You may use constraints grouping and group sequences to define the validation order. According to JSR-303 (part 3.5. Validation routine):
Unless ordered by group sequences, groups can be validated in no
particular order. This implies that the validation routine can be run
for several groups in the same pass.
As Hibernate Validator documentation says:
In order to implement such a validation order you just need to define
an interface and annotate it with #GroupSequence, defining the order
in which the groups have to be validated (see Defining a group
sequence). If at least one constraint fails in a sequenced group, none
of the constraints of the following groups in the sequence get
validated.
First, you have to define constraint groups and apply them to the constraints:
public interface CheckItFirst {}
public interface ThenCheckIt {}
public class FormModel {
#NotBlank
private String name;
#NotNull(groups = CheckItFirst.class)
#ImageSizeConstraint(groups = ThenCheckIt.class)
private MultipartFile image;
}
And then, as constraints are evaluated in no particular order, regardless of which groups they belong to (Default group too), you have to create #GroupSequence for your image field constraints groups.
#GroupSequence({ CheckItFirst.class, ThenCheckIt.class })
public interface OrderedChecks {}
You can test it with
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<FormModel>> constraintViolations =
validator.validate(formModel, OrderedChecks.class);
To apply this validation in Spring MVC Controller methods, you may use the #Validated annotation, which can specify the validation groups for method-level validation:
#PostMapping(value = "/processFormModel")
public String processFormModel(#Validated(OrderedChecks.class) FormModel formModel) {
<...>
}
Easy just return true for isValid if image is null for your custom constraint.
Read the specification of JSR-303, you will see that this is normal behaviour and it makes sense as there is "NotNull".

Hibernate Validator validate set

I have custom validation annotation called #Role and I have custom validator which validates User and it works. Problem is when I want to use this validator on Set<User> to validate every element on this set like this
public class Project {
// Validates
#Role
private User creator;
// Throws error
#Role
private Set<User> users;
}
This sadly throws this error:
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'cz.studenthub.validators.annotations.Role' validating type 'java.util.Set<cz.studenthub.core.User>'
Is there any way to validate collections without having to write new validator for Set<User>?
Note: I don't want to use #Valid for User because that instance doesn't need to be completely valid, it just needs to meet that one condition validated by my RoleValidator.
I would suggest you to use Hibernate Validator 6.0.0.Beta2 if you can.
We now have support for container element constraints so you can simply do:
public class Project {
// Validates
#Role
private User creator;
private Set<#Role User> users;
}
We are planning a Candidate Release 1 in a week so it's already pretty stable.

Java spring annotation attribute

I have a post method receiving an object as parameter, in this object I have an attribute with annotations #ValidDate and #NotEmpty.
in another method I want to use the same object but I just want annotation #ValidDate on the attribute.
It's possible ?
the attribute :
#NotEmpty
#ValidDate
private String installDate;
the function :
public String findLinksByCriteria(#Valid #ModelAttribute LinkForm link, BindingResult bindingResult, Model uiModel) {
if (bindingResult.hasErrors()) {
return ViewConstants.LINK_SEARCH_VIEW;
}
Probably one of these thing with multiple solutions. You can remove the optional constraint and do it manually however if you want to keep it strictly within the context of the Bean Validation API then you can do it using validation groups.
https://docs.oracle.com/cd/E19798-01/821-1841/gkahp/index.html
Constraints may be added to one or more groups. Constraint groups are
used to create subsets of constraints, so only certain constraints
will be validated for a particular object. By default, all constraints
are included in the Default constraint group.
By using Spring's #Validated annotation rather than #Valid you can specify one or more groups of constraints to be applied for any given case.
There is a detailed example here:
http://blog.codeleak.pl/2014/08/validation-groups-in-spring-mvc.html

Read only fields in spring-roo or spring-web-mvc

I have what appears to be a common problem within spring-mvc. Several of my domain object have fields that are not updatable so in my view I am not binding these fields.
For competeness sake The way these are excluded from the view is by editing the spring-roo scaffolded view setting the render attribute on the parameter to false.
As spring-mvc creates a new instance of the object rather than updating the existing object these fields are null. This means however that the object fails its validation before the control reaches the controller.
A lot of my entities will have extra fields that are not updatable in the view so I'd like to be able to come up with a generic solution rather than continually doing the same work over and over again (violating DRY).
How can one allow validation to occur in a consistent manner if fields are omitted from the view?
#RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String UserController.update(#Valid User user, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
populateEditForm(uiModel, user);
return "admin/users/update";
}
uiModel.asMap().clear();
user.merge();
return "redirect:/admin/users/" + encodeUrlPathSegment(user.getId().toString(), httpServletRequest);
}
Possible Solutions:
Omit #Valid annotation from the controller.
Pros
Easy to implement.
Easy to understand.
Cons
Means changing the controller method for every update on every object.
Validation is not occuring in the same place as all of the rest of the application.
No easy way to return the binding errors back to the view (need to validate the object afterwards)
Add Custom Validator for methods that need omitted fields
Example:
#InitBinder
public void initBinder(WebDataBinder binder, HttpServletRequest request) {
if (request.getMethod().equals("PUT")) {
binder.setDisallowedFields("registrationDate", "password");
Validator validator = binder.getValidator();
Validator userUpdateValidator = new UserUpdateValidator();
binder.setValidator(userUpdateValidator);
}
}
Pros
Clear flow.
Cons
Suffers wildly from DRY problems. This means that If the domain object is altered in any way I need to revalidate.
Field validation is not the same as Hibernate validation when saving.
No tangible benefits over omitting validation and manually validating.
Would consider if?
Custom validator could delegate to standard JSR-303 validator but just omit fields.
Remove JSR-303 annotations from the domain object
Not an option this means that there is no validation on an object before saving. Worse I believe it will affect the DDL that is producted for database, removing constraints from the DB itself. Only put in here for completeness sake
Lookup domain object before validation occurs
The idea of this solution is to lookup the existing domain object before updating. Copying any not null fields to the old object from the request.
Pros
- The validation can go through the normal cycle.
- The validation doesn't need to change depending on what method you are implying.
Cons
Database access before hitting the controller has a bit of a smell.
I can't see any way to implement this.
Won't work for fields that need to be omitted during other stages of the object lifecycle. For example if adding a timestamp during creation.
I would like to know how to implement either a validator that delegates to the standard JSR-303 validator or alternatively how to lookup the object before modifying it. Or if anyone has any other possible solutions?
Either of these solutions allow for the treatment to be consistent over multiple objects.
Hopefully either would allow for added annotations such as.
#RooCreateOnly which means the domain object could be annotated as such leaving all the validation definitions in the one place.
The last option can be achieved with the #ModelAttribute annotation.
Create a method that returns your domain object and add the #ModelAttribute annotation to it. Then add the same annotation to the domain object argument of the method where you want to use that object. Spring will first load the object from the ModelAttribute method then merge it with the posted data.
Example:
#ModelAttribute("foobar")
public User fetchUser() {
return loadUser();
}
#RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String update(#ModelAttribute("foobar") #Valid User user, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
return etc();
}
You can use the disabled property for the input tags in your jspx file containing the form for the fields that you want to mark as read-only.
Also make sure you clear the z attribute relating the field so that Roo will ignore the tag if there is any change made to the entity later on.
Cheers!
I'm posting another answer totally unrelated to my previous one.
There is another solution: wrap your domain object into special form object that only expose the fields you want to validate.
Example:
public class UserForm {
private final User user = new User();
// User has many fields, but here we only want lastName
#NotEmpty // Or whatever validation you want
public String getLastName() {
return this.user.getLastName();
}
public void setLastName(String lastName) {
this.user.setLastName(lastName);
}
public User getUser() {
return this.user;
}
}

Categories