Hi everybody,
I am implementing a framework that requires a class-level Customs Validator to verify temporal data. It looks like the ClockProvider
is the recommended way to verify fields that are time sensitive.
Now, my root bean contains a field that holds the LocalDateTime
value that I must use in my ClockProvider
, if the value is null, then, I will use the system’s current date and time. Besides, It also needs to be available when cascading within the object graphs.
Let me give you a non-real example of domain objetcs to explain what I need to achieve:
public class Parent {
String fullname;
LocalDateTime birthdate;
List<@Valid Child> children;
}
public class Child {
String fullname;
@Future LocalDateTime birthdate;
}
My validator must compare that the Child.birthdate
is after Parent.birthdate
.
- Is it possible to achieve this scenario?
- If so, How can I implement a ClockProvider and set the value of my root bean i.e.
Parent.birthdate
?
Hey @floresm.leonardo
Looking at your use case, it doesn’t seem like you need a ClockProvider
. If you would want to go with the class level constraint, it’ll be something like:
public class CustomAnnotationConstraintValidator implements ConstraintValidator<YourClassLevelAnnotation, Parent> {
@Override
public boolean isValid(Parent value, ConstraintValidatorContext context) {
// check for nulls etc ...
for ( Child child : value.children ) {
if ( value.birthdate.isBefore( child.birthdate ) ) {
return false;
}
}
return true;
}
}
A clock provider is more about obtaining and using a clock to get a temporal instance from the clock. For example, in Future/Past constraints, get the current time to compare to the validated value.
See Hibernate Validator 8.0.0.Final - Jakarta Bean Validation Reference Implementation: Reference Guide
and Hibernate Validator 8.0.0.Final - Jakarta Bean Validation Reference Implementation: Reference Guide for an example on how to use clock provider.
Hi @mbekhta,
Thank you for your time and response. The use case was to demonstrate the need for a ClockProvider. I am building a toolkit and will not know the Domain Model that the developer will build. I will know that its Data Model must have a field to denote the LocalDateTime
used in my ClockProvider. This field will be annotated with one annotation provided by the framework and used to query N amount of temporal tables.
In summary, the domain object will have reference data (temporal data) from another business module that holds more than a hundred (100+) tables. I am trying to automate the validation process to make life easier for the developer. For example:
public class RegistrationForm {
String fullname;
@ReferenceDateTime
LocalDateTime registrationDateTime;
@Valid Payment payment;
}
@ReferenceData(code="exchange-rate", keys {"foreignCurrencyCode","nationalCurrencyCode"}, target="rate")
public class Payment {
String foreignCurrencyCode;
String nationalCurrencyCode;
BigDecimal rate;
BigDecimal foreignAmount;
BigDecimal localAmount;
}
The @ReferenceData
is a ConstraintValidator
to verify keys exist in the temporal table, in this case, the exchange rate for the pair foreignCurrencyCode
and nationalCurrencyCode
, and copy the appropriate value to the property rate
. As exchange rate vary from day to day (temporal data), I need to use the RegistrationForm.registrationDateTime
for this purpose (that belong to the root bean).
I know that I can do all this programmatically, but I want to offer a simplified tool to avoid boilerplate code and speed up the development.
I hope this help to clarify my needs and to see if Bean Validation can help me to achieve it.
THanks again for your help
Hey Leonardo,
Hmm… Generalizing the problem, you either want to push some data from a parent object into the validation context of a child or to pull the data from a parent object from a validation context of a child when cascading validation is executed…
The thing is that a single constraint validation is evaluated within an isolated context, so it only knows about the object it is currently validating…
I cannot think of a clean way to achieve this with what the validator can currently offer… But here are a few ideas you might want to explore…
If you have control over your classes, then you could try to make properties aware of their parent classes holding them, be it passing the parent into the constructor (new Payment( registrationFormInstance )
) or making them inner classes (and accessing them through something like RegistrationForm.this.
). This way, when you are validating an instance of the Payment
class, you will have access to the RegistrationForm
and will be able to access the date field from it.
You can explore the ScriptAssert
constraint (Additional constraints). You could put this @ScriptAssert
on the RegistrationForm
and the script will be something along the lines of the class validator from the previous example.
Now to make it more generic … you can try to make use of the programmatic constraint definition. You could create a script for the "@ScriptAssert` depending on the class you are about to validate then use the programmatic constant definition to declare a script assert on the class and then create a validator do the validation and move to the next thing … To make it at least a bit more performant you’d probably want to group this somehow, so you don’t need to define constraint and create a new validator for each validated object
Marko,
Again, thank you for your time and ideas. I will definitely explore them. Especially the programmatic constraint…