Unique constraint with JPA and Bean Validation - java

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.

Related

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.

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

Confusion: #NotNull vs. #Column(nullable = false) with JPA and Hibernate

When they appear on a field/getter of an #Entity, what is the difference between them? (I persist the Entity through Hibernate).
What framework and/or specification each one of them belongs to?
#NotNull is located within javax.validation.constraints. In the javax.validation.constraints.NotNull javadoc it says
The annotated element must not be null
but it does not speak of the element's representation in the database, so why would I add the constraint nullable=false to the column?
#NotNull is a JSR 303 Bean Validation annotation. It has nothing to do with database constraints itself. As Hibernate is the reference implementation of JSR 303, however, it intelligently picks up on these constraints and translates them into database constraints for you, so you get two for the price of one. #Column(nullable = false) is the JPA way of declaring a column to be not-null. I.e. the former is intended for validation and the latter for indicating database schema details. You're just getting some extra (and welcome!) help from Hibernate on the validation annotations.
The most recent versions of hibernate JPA provider applies the bean validation constraints (JSR 303) like #NotNull to DDL by default (thanks to hibernate.validator.apply_to_ddl property defaults to true). But there is no guarantee that other JPA providers do or even have the ability to do that.
You should use bean validation annotations like #NotNull to ensure, that bean properties are set to a none-null value, when validating java beans in the JVM (this has nothing to do with database constraints, but in most situations should correspond to them).
You should additionally use the JPA annotation like #Column(nullable = false) to give the jpa provider hints to generate the right DDL for creating table columns with the database constraints you want. If you can or want to rely on a JPA provider like Hibernate, which applies the bean validation constraints to DDL by default, then you can omit them.
The JPA #Column Annotation
The nullable attribute of the #Column annotation has two purposes:
it's used by the schema generation tool
it's used by Hibernate during flushing the Persistence Context
Schema Generation Tool
The HBM2DDL schema generation tool translates the #Column(nullable = false) entity attribute to a NOT NULL constraint for the associated table column when generating the CREATE TABLE statement.
As I explained in the Hibernate User Guide, it's better to use a tool like Flyway instead of relying on the HBM2DDL mechanism for generating the database schema.
Persistence Context Flush
When flushing the Persistence Context, Hibernate ORM also uses the #Column(nullable = false) entity attribute:
new Nullability( session ).checkNullability( values, persister, true );
If the validation fails, Hibernate will throw a PropertyValueException, and prevents the INSERT or UPDATE statement to be executed needesly:
if ( !nullability[i] && value == null ) {
//check basic level one nullablilty
throw new PropertyValueException(
"not-null property references a null or transient value",
persister.getEntityName(),
persister.getPropertyNames()[i]
);
}
The Bean Validation #NotNull Annotation
The #NotNull annotation is defined by Bean Validation and, just like Hibernate ORM is the most popular JPA implementation, the most popular Bean Validation implementation is the Hibernate Validator framework.
When using Hibernate Validator along with Hibernate ORM, Hibernate Validator will throw a ConstraintViolation when validating the entity.
Interesting to note, all sources emphasize that #Column(nullable=false) is used only for DDL generation.
However, even if there is no #NotNull annotation, and hibernate.check_nullability option is set to true, Hibernate will perform validation of entities to be persisted.
It will throw PropertyValueException saying that "not-null property references a null or transient value", if nullable=false attributes do not have values, even if such restrictions are not implemented in the database layer.
More information about hibernate.check_nullability option is available here: http://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/Hibernate_User_Guide.html#configurations-mapping.

Programmatic Bean Validation (JSR 303) without Annotation [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
using Hibernate Validator without calling annotation.
I have this composite constraint annotation (only for illustration):
#Target... #Retention...
#Constraint(validatedBy = {})
#Pattern(regexp = PasswordComplexity.AT_LEAST_TWO_NONE_ALPAH_CHARS)
#Length(min = 6, max = 20)
public #interface PasswordComplexity {
...
}
And I use it in Spring Controllers and Entity Classes.
But now I need to check a single String in a Service method, where I need to apply the same constraint to a single String. Because of the fact that the constraint is the same, I want to use the same definition of the constraint (#PasswordComplexity) (single source of truth). Something like:
public void createUser(UserDto userDto, String password) {
if(hasViolation(validator.validate(password,PasswordComplexity.class))) {
throw new PasswordComplexityViolationException();
} else {
…
}
}
But I do not know how to run the JSR 303 Validator for an not annotated simple object (String). Is it at least possible, and how?
(I use Hibernate Validator as JSR 303 provider)
One way to do this would be write a full custom validator, and push the logic down into that class having the annotation just use the validator. This would mean you then had an independent compilation unit (A full class PasswordComplexityValidator implements implements ConstraintValidator<PasswordComplexity, String> ...) which you could use independently of the annotation. This approach would also make it easier for you to unit test the validation.
However, since you are using the annotation as a way of configuring the existing regex validator provided by Hibernate, you could use that one instead, passing it the constant pattern from the annotation class. You should also be able to package your length constrain into the regex too, which would be simpler and faster than having both annotations anyway.

Categories

Resources