Hibernate Validator - optional validation depending on lifecycle - java

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);

Related

Bean validation #ElementCollection and #Version conflict and fails validation

I am facing a very strange issue at the moment.
I have an entity that contains a property that is an element collection.
#ElementCollection(targetClass=Integer.class, fetch = FetchType.EAGER)
#CollectionTable(name="campaign_publisher", joinColumns=#JoinColumn(name="campaign_id"))
#Column(name = "publisher_id")
...
#NotEmpty(message = "campaign.publishers.missing")
public Set<Integer> getPublishers() {
return this.publishers;
}
public Campaign setPublishers(Set<Integer> publisherId) {
this.publishers = publisherId;
return this;
}
This all works fine. The values are validated and saved correct.
I also want this entity to have optimistic concurrency so I applied a #Version annotation as well.
#Version
private Long etag = 0L;
...
public Long getEtag() {
return etag;
}
public void setEtag(Long etag) {
this.etag = etag;
}
By adding the #Version annotation the #NotEmpty validation on my set of publishers always returns invalid.
To try and diagnose this I have tried the following:
Creating a custom validator at the entity level so I can inspect the values in the entity. I found that the Set of values have been replaced with an empty PersistentSet which is causing the validation to always fail.
I created some unit tests for the entity that uses a validator that is retrieved from the validationfactory and this validator seems to work as expected.
I have also tried to change the ElementCollection to a many-to-many relationship and a bi-directional one-to-many but the issue persists.
Right now I am out of ideas. The only thing I have found that works correctly is disabling the hibernate validation and manually calling the validator just before I save my data.
So my questions are:
Has anyone encountered this issue before?
Any advice on what I could try next?
Thank you all for reading!
Short answer: Set the initial value for etag = null.
// this should do the trick
#Version
private Long etag = null;
Longer one : When you are adding a optimistic locking via adding #Version annotation on a field with a default value you are making hibernate/spring-data think that the entity is not a new one (even the id is null). So on initial save instead of persisting entity undelying libraries try to do a merge. And merging transient entity forces hibernate to just one by one copy all the properties from source entity (the ones which you are persisting) to the target one (which is autocreate by hibernate with all the properties set to default values aka nulls) and here comes the problem, as hibernate will just copy the values of associations of FROM_PARENT type or in other words only associations which are hold on entity side but in your case the association is TO_PARENT (a foreign key from child to parent) hibernate will try to postpone association persistance after main entity save, but save will not work as entity will not pass #NotEmpty validation.
First I would suggest to remove the default value initialization for your #Version property. This property is maintained by hibernate, and should be initialized by it.
Second: are you sure that you are validating the fully constructed entity? i.e. you are constructing something, then do something, and for exact persist/flush cycle your entity is in wrong condition.
To clarify this, while you are on a Spring side, I would suggest to introduce service-level validation on your DAO layer. I.e. force the bean validation during initial call to DAO, rather then bean validation of entity during flush (yeap hibernate batches lots of things, and exact validation happens only during flush cycle).
To achieve this: mark your DAO #Validated and make your function arguments beign validated: FancyEntity store(#Valid #NotNull FancyEntity fancyEntity) { fancyEntity = em.persist(fancyEntity); em.flush(); return fancyEntity;}
By making this, you will be sure that you are storing valid entity: the validation would happen before store method is called. This will reveal the place where your entity became invalid: in your service layer, or in bad behaving hibernate layer.
I noticed that you use mixed access: methods and fields. In this case you can try to set #Version on the method:
#Version
public Long getEtag() {
return etag;
}
not on the field.

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.

javax.validation - validation context (for caching purpose)

I am using Hibernate validators to run validation constraints defined by javax.validation API. Everything runs on the Spring 3.
I defined my custom constraints:
#ProjectExists
#ProjectActive
#ProjectCommentable
and my custom validators that covers above constraints.
Now I would like to validate following DTO:
public class Comment {
private String content;
#ProjectExists
#ProjectActive
#ProjectCommentable
private String projectName;
}
The problem is that each validator has to run database query, find project and do something with it.
I am looking for solution which allows to cache project that was found before. The best solution would be to share something like "validation context" between all validators, so that I could set Project to validation context.
Next problem is that after validation DTO is transformed to data model, so the "find project query" has to be called once again. It would be good to reuse cached project also here.

Spring Mongo Exception - Customizing field name for id property not allowed! Custom name will not be considered

I have been using Spring-Data-Mongo for a while now on a project without issues. In the last POM update, I started seeing the following exception in the log file:
WARN : 04 Aug 2014 13:55:24
org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty
- Customizing field name for id property not allowed! Custom name will not be considered!
It does not provide any clue as to where this issue arises so we are clueless. It is repeated several times as calls to the app are made. We did find the origin of this here: https://github.com/spring-projects/spring-data-mongodb/blob/11417144bd3574c35af06fde3a3c2e56a1dd5890/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java#L85
Any ideas?
Edit:
Added example class for those interested:
#Document(collection="Account")
public class Account {
....
#Id
private String id = null;
....
}
I listened to the nice comments above and put a breakpoint on . I quickly found out that one of my classes was using the following:
#Id
private String Id;
Note the Id instead of id. Once changed, the warning disapearind.
I fully support the suggestion to have spring-data provide a proper report rather than simply a meaningless warning.
My thought is that you are trying to use #Id with #Field on the same field. This is not allowed since the id field in Mongo must be called _id. Anything further is hard to tell since you didn't post your code.

Unique constraint with JPA and Bean Validation

I'd like to have a #Unique constraint with Bean Validation, but that is not provided by the standard. If I would use JPA's #UniqueConstraint I wouldn't have a unique validation and error reporting mechanism.
Is there a way to define #Unique as a Bean Validation constraint and combine it with JPA, such that JPA creates a column with an unique constraint and checks wheter a value is unique or not?
Unless you acquire a lock on a whole table, it is basically not possible to check for unicity using a SQL query (any concurrent transaction could modify data after a manual check but before the commit of the ongoing transaction). In other words, it isn't possible to implement a valid unique verification at the Java level and thus to provide a validation implementation. The only reliable way to check for unicity is while committing the transaction.
The BV spec summarizes it like this:
Appendix D. Java Persistence 2.0 integration
Question: should we add #Unique that
would map to #Column(unique=true)?
#Unique cannot be tested at the Java
level reliably but could generate a
database unique constraint generation.
#Unique is not part of the BV spec
today.
So while I agree that it would be nice to have unique (and non null) constraint violations wrapped in a Bean Validation exception, this is currently not the case.
References
Bean Validation specification (JSR 303)
Appendix D. Java Persistence 2.0 integration
Question about validation and persistence constraints
More information on how to implement a #Unique and the problematic around it can be found here - http://community.jboss.org/wiki/AccessingtheHibernateSessionwithinaConstraintValidator
Well you CAN do it, but it's not trivial. The problem is: the validator requires database access to perform some queries to check, if the value you want to insert is already there or not. And this can't be really done from the validator, as it doesn't have access to the sessionFactory/session. Of course you could instantiate it (session/sessionFactory) inside the validator, but it's not a good coding practice.
You can make a validator read the JPA annotations and apply it. Here is somewhat of an example using spring validators that can be used as an idea to expand on.
JPA JSR303 Spring Form Validation
You can also inject (#Inject or Spring's #Autowired) your session bean in a custom validator and the container should know how to wire it up. I only know this as a Spring example:
import javax.validation.ConstraintValidator;
public class MyConstraintValidator implements ConstraintValidator {
#Autowired //#Inject
private Foo aDependency;
...
}
You should try (insert or update), catch the exception and do some action. For example in a JSF backing bean :
try {
dao.create(record);//or dao.modify(record)
//add message success
} catch(EJBException e) {
//look for origin of error (duplicate label, duplicate code, ...)
var err = dao.isUnique(record);
if(err == null) throw e;//other error
String clientId = null;
String message = null;
switch(err) {
case CODE:
clientId = "client_id_of_input_code";
message = "duplicate code";
break;
case LABEL:
clientId = "client_id_of_input_label";
message = "duplicate label";
break;
default:
throw new AssertionError();//or something else
}
facesContext.addMessage(clientId, new FacesMessage(FacesMessage.SEVERITY_ERROR, message));
facesContext.validationFailed();
}
Another option is to check before the insertion/modification. This can be time consuming and doesn't prevent the error to happen in the end.

Categories

Resources