Hibernate validations on save (insert) only - java

We encountered a problem with legacy code. There is a validation set for a "username" field, validating its length and making sure it contains at least one letter:
#Column(name = "username")
#Size(min = 4, max = 40)
#Pattern(regexp = "^.*[a-zA-Z]+.*$")
private String username;
The problem we have is that some existing legacy data do not fit these validations, and I'm trying to find a way to make these validations to be ignored for legacy data (old users), while still be applied to newly created users.
I was thinking about moving the validations to setUsername(...) method (so value will be validated on an actual change only), but this caused an exception:
javax.validation.ValidationException: Annotated methods must follow the JavaBeans naming convention. setUsername() does not.
I also made sure the entity is set to dynamicUpdate=true, but this doesn't help since hibernate is validating all properties, even if no change occurred.
How can I prevent these validations to be performed on existing entities during update?
I do not want the fix to impact other properties validations on the same entity and I can't change hibernate configuration.

After two days of research I found out how to make this work.
Apparently, specifying validations that would be validated on INSERT only is not that difficult. The only changes required are to set these validations to a specific validation group and to validate this group during INSERT/pre-persist events.
First of all I created an interface called platform.persistence.InsertOnlyValidations to be used as a group which will be validated during pre-persist only.
Than, I added the group to the username field validations:
#Column(name = "username")
#Size(min = 4, max = 40, groups = {InsertOnlyValidations.class})
#Pattern(regexp = "^.*[a-zA-Z]+.*$", groups = {InsertOnlyValidations.class})
private String username;
This instructs hibernate not to use these validations as part of the default group. Now, I needed to instruct hibernate to validate these validation rules during insert only.
The way to do that is very simple, I needed to pass the property javax.persistence.validation.group.pre-persist, while indicating which groups will be validated during a pre-persist event:
javax.persistence.validation.group.pre-persist=javax.validation.groups.Default,platform.persistence.InsertOnlyValidations
This instructs hibernate that during a pre-persist event all default validations will be validated (javax.validation.groups.Default) in addition to all the validations included in the InsertOnlyValidations group.

Related

How to set #NotEmpty annotation messages in Spring?

In a Java (+ Spring Boot) project, there is a notation using javax.validation as shown below:
#NotEmpty(message = "validation.product.notEmpty")
private String product;
#NotEmpty(message = "validation.username.password")
private String password;
I have a look at the usage of them, but there are some points that I could not understand:
1. Is there a special usage e.g. conditional message displaying for validation.username.password? For example if username field is null, then display this message? Or is it completely the same manner as the product field?
2. I search the project, but could not find validation.product.notEmpty or validation.username.password. So, how do they work? I think there should be a definition for these messages, but as I did not find, is it come from default messages of javax.validation?
What is the difference between #EmailRegex and #Email? And is there
any need to also use #NotEmpty with these #EmailRegex or #Email
annotations?
#Email will not throw error for an empty String. So you need #NotEmpty to be sure that this String is not empty if you always require an email to be there.
#Email will consider valid everything that is in the form blabla#blabla.blabla. If you want to further constraint this you can use #EmailRegex so that you allow only blabla#blabla.eu by defining your own regular expression.
#EmailRegex does not seem to be included in hibernate annotations or spring annotations. So it is either a custom annotation imported from somewhere else or just a custom annotation of your application. Inspect the code to see how it actually behaves but from it's name I suppose it behaves as I have explained above.
I search the project, but could not find validation.product.notEmpty
or validation.username.password. So, how do they work? I think there
should be a definition for these messages, but as I did not find, is
it come from default messages of javax.validation?
It should be with {....} so like #NotEmpty(message = "{validation.username.password}") private String password;. In that case Spring will automatically read properties from the property files and apply the value for the property validation.username.password. If it does not exist then go to either application.properties or application.yaml and add that property.
Some more notes on this last one. I have seen some strange cases in backend-frontend applications which might be your case here.
#NotEmpty(message = "validation.username.password")
The actual message thrown here when the validation fails is validation.username.password. I have seen cases where the frontend then reads that message and binds a value to this one. I have seen this to be used when frontend supports multiple languages and binds another value for each language each time. This would explain why you don't have { } or such a property in your application.
#NotEmpty(message = "{validation.username.password}")
with an existing property validation.username.password= password can not be empty
will have as a result when the validation fails the message password can not be empty to be delivered.

Can Java Validator method validateProperty validate object with multiple fields inside?

As stated in the title, I'm using the Validator to validate fields based on their names like this:
mandatoryInputs.stream()
.map(x -> v.validateProperty(accountBenefitForm, x, AccountBenefitFormAdditionalInfo.class))
it works fine, but only for the simple fields like Strings that have their constraints in the accountBenefitForm for example:
#NotBlank(message = "Username can not be null.", groups = {AccountBenefitFormBasicInfo.class})
#Size(max = 255, message = "Username is too long (max size 255).")
private String username;
But it won't work for objects that have multiple fields inside them, like this one:
#Valid
private ContactData contactData;
where ContactData implementation looks like this:
#NotBlank(message = "You have to add e-mail address.", groups = {AccountBenefitFormAdditionalInfo.class})
#Email(message = "E-mail is not valid.", groups = {AccountBenefitFormAdditionalInfo.class})
#Size(max = 255, message = "E-mail is too long (max size 255).", groups = {AccountBenefitFormAdditionalInfo.class})
private String email;
#NotBlank(groups = {AccountBenefitFormAdditionalInfo.class})
private String phoneNumber;
Is there a way I can make this work or do I need to validate those more complex objects on their own?
You have basically two kinds of annotations that can be used for validations here: Spring annotations (#Validated) as well as the javax annotation (#Valid, #NotBlank) etc.
For Spring, you can luckily often skip the manual validation unless you have some custom constraints (e.g. if person lives in country ABC, they need to provide additional info). Annotating just the field is not enough if you don't cascade the validation from the outer class. This cascade can be done conveniently on method-level by annotating the method param with #Valid e.g.
void doSomething(#Valid ContactDataHolder contactDataHolder) { ... }
If you'd like to use validation in Spring, I would recommend to use the Spring Validator interface instead of the one from javax as it should give you the expected behavior for nesting. You might also decide to apply #Validated on the class level to save you from writing #Valid(ated) on the method level each time.
So I've managed to somewhat resolve my problem by using the Apache BVal. Heres the code to create a validator to use the validateProperty method with cascading validation enabled:
ValidatorFactory factory = Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory();
CascadingPropertyValidator validator = factory.getValidator().unwrap(CascadingPropertyValidator.class);
validator.validateProperty(accountBenefitForm, x, true, Default.class, AccountBenefitFormAdditionalInfo.class))
where x is the string of field to validate, and if that field is annotated with #Valid, it will then validate the inside fields according to their own constraints.
Along the way I've also found out that you can just use the "normal" javax Validator and pass the field to validate as contactData.email which means validate email field of the contactData field of the object that u pass as first argument to the validateProperty method.
Edit:
BVal supports Bean Validation 1.1 (JSR 349) and not the 2.0 version(JSR 380), and since #NotBlank or #NotEmpty constrains are part of 2.0, it won't validate a field annotated with them. Here are the docs for the 1.1 , and 2.0

Spring JPA - RESTful partial update and validation for entity

I've a simple RESTful API based on Spring MVC using a JPA connected MySQL database. Until now this API supports complete updates of an entity only. This means all fields must be provided inside of the request body.
#ResponseBody
#PutMapping(value = "{id}")
public ResponseEntity<?> update(#Valid #RequestBody Article newArticle, #PathVariable("id") long id) {
return service.updateById(id, newArticle);
}
The real problem here is the validation, how could I validate only provided fields while still require all fields during creation?
#Entity
public class Article {
#NotEmpty #Size(max = 100) String title;
#NotEmpty #Size(max = 500) String content;
// Getters and Setters
}
Example for a partial update request body {"content": "Just a test"} instead of {"title": "Title", "content": "Just a test"}.
The actual partial update is done by checking if the given field is not null:
if(newArticle.getTitle() != null) article.setTitle(newArticle.getTitle());
But the validation of course wont work! I've to deactivate the validation for the update method to run the RESTful service. I've essentially two questions:
How can I validate only a "existing" subset of properties in the
update method while still require all fields during creation?
Is there a more elegant way for update partially then checking for
null?
The complexity of partial updates and Spring JPA is that you may send half of the fields populated, and even that you will need to pull the entire entity from the data base, then just "merge" both entity and the pojo, because otherwise you will risk your data by sending null values to the database.
But merging itself is kind of tricky, because you need to operate over each field and take the decision of either send the new value to the data base or just keep the current one. And as you add fields, the validation needs to be updated, and tests get more complex. In one single statement: it doesn't scale. The idea is to always write code which is open for extension and closed for modifications. If you add more fields, then the validation block ideally doesn't need to change.
The way you deal with this in a REST model, is by operating over the entire entity each time you need. Let's say you have users, then you first pull a user:
GET /user/100
Then you have in your web page the entire fields of user id=100. Then you change its last name. You propagate the change calling the same resource URL with PUT verb:
PUT /user/100
And you send all the fields, or rather the "same entity" back with a new lastname. And you forget about validation, the validation will just work as a black box. If you add more fields, you add more #NotNull or whatever validation you need. Of course there may be situations where you need to actually write blocks of code for validation. Even in this case the validation doesn't get affected, as you will have a main for-loop for your validation, and each field will have its own validator. If you add fields, you add validators, but the main validation block remains untouchable.

How to use different validation rules on same entity in Hibernate?

Problem:
How to save object Account as nested object when only ID is needed without getting ConstraintValidator exception?
Problem is because i have set validation rules to class, but when i want to save sem entity as nested object i get exception that some property values are missing. So i would liek to have different validation rules when i want to persist object as a whole and when i want to use it only sa nested object (when only ID is needed).
public class Account {
private int id;
#NotNull
private String name;
#NotNull
private String lastName;
#NotNull
private String userName;
//getters&setters
If I include Account as nested object i just need ID to be able to use it as FK (account entity is already in DB), but because of #NotNull annotation i get Exception.
Is there a way to ignore those annotations from Account when trying to save object Shop or how to create different validation rules for Account to validate just soem other properties and not all?
public class Shop {
private int id;
private Account owner; // only ID is needed
Do you have any basic example? I dont understand those in documentation. I have already read documentation before posting here.
You want to look at Bean Validation groups where you can classify specific validations so they are only activated when that group is validated and ignored otherwise.
You can refer to the documentation here for details.
Taking an example from the documentation:
// This is just a stub interface used for tagging validation criteria
public interface DriverChecks {
}
// The model
public class Driver {
#Min(value = 18, message = "You must be 18", groups = DriverChecks.class)
private int age;
// other stuffs
}
A group is nothing more than a tag that allows you to enable/disable validations based on specific use cases at run-time. By not specifying the groups attribute on a bean validation annotation, it defaults to the Default group, which is what Bean Validation uses if a group-tag isn't specified at the time of validation.
That means the following holds true:
// Age won't be validated since we didn't specify DriverChecks.class
validator.validate( driver );
// Age will be validated here because we specify DriverChecks.class
validator.validate( driver, DriverChecks.class );
This works great when you're triggering the validation yourself inside your service methods because you can manually control which group checks are applicable based on that method's use case.
When it comes to integrating directly with Hibernate ORM's event listeners that can also trigger bean validation, group specifications become a bit harder as they must be specified based on the event-type raised by hibernate.
javax.persistence.validation.group.pre-persist
javax.persistence.validation.group.pre-update
javax.persistence.validation.group.pre-remove
For each of the above properties you can specify in the JPA properties supplied to Hibernate, you can list a comma delimited list of groups that are to be validated for each of those event types. This allows you to have varying checks during insert versus update versus removal.
If that isn't sufficient, there is always the fact that you can create your own constraint validator implementation and annotation to plug into Bean Validation and specify that at the class or property level.
I have often found this useful in cases where values from multiple fields must be validated as a cohesive unit to imply their validity as the normal field-by-field validations didn't suffice.

Hibernate Validator - optional validation depending on lifecycle

Just started using Hibernate Validator. I have a case where a bean's id is autogenerated when saved. I'd live to validate the bean before the save. At which time the id can be null. However, when I want to update it the id must be notnull.
So the generic #NotNull on the field won't work because when I go to save it it will fail validation.
There are ways to work around this, but I was wondering if the spec or hibernate implementation have a standard way of doing this. I'd like to not have any validation errors on save and no validation on update.
Such as applying a constraint but it's ignored unless implicitly named or something like that.
Thanks in advance.
You can achieve that with groups.
public class MyBean {
#NotNull(groups = UpdateBean.class)
private Long id;
}
Validate without the id:
validator.validate(myBean);
Validate with the id:
validator.validate(myBean, UpdateBean.class);

Categories

Resources